]> git.saurik.com Git - apple/mdnsresponder.git/commitdiff
mDNSResponder-164.tar.gz mac-os-x-1051 v164
authorApple <opensource@apple.com>
Fri, 2 Nov 2007 22:13:42 +0000 (22:13 +0000)
committerApple <opensource@apple.com>
Fri, 2 Nov 2007 22:13:42 +0000 (22:13 +0000)
19 files changed:
Clients/dns-sd.c
Makefile
mDNSCore/DNSCommon.c
mDNSCore/mDNS.c
mDNSCore/mDNSEmbeddedAPI.h
mDNSCore/uDNS.c
mDNSCore/uDNS.h
mDNSMacOSX/LegacyNATTraversal.c
mDNSMacOSX/daemon.c
mDNSMacOSX/mDNSMacOSX.c
mDNSMacOSX/mDNSMacOSX.h
mDNSPosix/Makefile
mDNSPosix/PosixDaemon.c
mDNSShared/PlatformCommon.c
mDNSShared/PlatformCommon.h
mDNSShared/dns_sd.h
mDNSShared/dnsextd.c
mDNSShared/uds_daemon.c
mDNSWindows/mDNSWin32.c

index 279e5ef2094cf790391f32d29258b00c18ef4822..718547cdcceb32b76402b3c6ce17bcddd50bf692 100644 (file)
@@ -799,10 +799,18 @@ static DNSServiceErrorType RegisterService(DNSServiceRef *sdref,
        return(DNSServiceRegister(sdref, flags, opinterface, nam, typ, dom, host, registerPort.NotAnInteger, (uint16_t) (ptr-txt), txt, reg_reply, NULL));
        }
 
+#define TypeBufferSize 80
+static char *gettype(char *buffer, char *typ)
+       {
+       if (!typ || !*typ || (typ[0] == '.' && typ[1] == 0)) typ = "_http._tcp";
+       if (!strchr(typ, '.')) { snprintf(buffer, TypeBufferSize, "%s._tcp", typ); typ = buffer; }
+       return(typ);
+       }
+
 int main(int argc, char **argv)
        {
        DNSServiceErrorType err;
-       char *dom;
+       char buffer[TypeBufferSize], *typ, *dom;
        int optind;
 
        // Extract the program name from argv[0], which by convention contains the path to this executable.
@@ -863,26 +871,29 @@ int main(int argc, char **argv)
                                        //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "dns-sd.ibm.com.", NULL);
                                        break;
 
-               case 'B':       {
-                                       char buffer[64], *typ;
-                                       typ = (argc < optind+1) ? "_http" : argv[optind+0]; // If no type argument, browse for advertised web pages
+               case 'B':       typ = (argc < optind+1) ? "" : argv[optind+0];
                                        dom = (argc < optind+2) ? "" : argv[optind+1];  // Missing domain argument is the same as empty string i.e. use system default(s)
+                                       typ = gettype(buffer, typ);
                                        if (dom[0] == '.' && dom[1] == 0) dom[0] = 0;   // We allow '.' on the command line as a synonym for empty string
-                                       if (!strchr(typ, '.')) { snprintf(buffer, sizeof(buffer), "%s._tcp", typ); typ = buffer; }
                                        printf("Browsing for %s%s%s\n", typ, dom[0] ? "." : "", dom);
                                        err = DNSServiceBrowse(&client, 0, opinterface, typ, dom, browse_reply, NULL);
                                        break;
-                                       }
 
                case 'L':       if (argc < optind+2) goto Fail;
+                                       typ = (argc < optind+2) ? ""      : argv[optind+1];
                                        dom = (argc < optind+3) ? "local" : argv[optind+2];
+                                       typ = gettype(buffer, typ);
                                        if (dom[0] == '.' && dom[1] == 0) dom = "local";   // We allow '.' on the command line as a synonym for "local"
-                                       printf("Lookup %s.%s.%s\n", argv[optind+0], argv[optind+1], dom);
-                                       err = DNSServiceResolve(&client, 0, opinterface, argv[optind+0], argv[optind+1], dom, (DNSServiceResolveReply)resolve_reply, NULL);
+                                       printf("Lookup %s.%s.%s\n", argv[optind+0], typ, dom);
+                                       err = DNSServiceResolve(&client, 0, opinterface, argv[optind+0], typ, dom, (DNSServiceResolveReply)resolve_reply, NULL);
                                        break;
 
                case 'R':       if (argc < optind+4) goto Fail;
-                                       err = RegisterService(&client, argv[optind+0], argv[optind+1], argv[optind+2], NULL, argv[optind+3], argc-(optind+4), argv+(optind+4));
+                                       typ = (argc < optind+2) ? "" : argv[optind+1];
+                                       dom = (argc < optind+3) ? "" : argv[optind+2];
+                                       typ = gettype(buffer, typ);
+                                       if (dom[0] == '.' && dom[1] == 0) dom[0] = 0;   // We allow '.' on the command line as a synonym for empty string
+                                       err = RegisterService(&client, argv[optind+0], typ, dom, NULL, argv[optind+3], argc-(optind+4), argv+(optind+4));
                                        break;
 
                case 'P':       if (argc < optind+6) goto Fail;
index f19fe5a47b38be1650c2adbc038a75171573d454..d774618acc6554df31812f67defd29210d1d8e06 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -16,7 +16,7 @@
 
 include /Developer/Makefiles/pb_makefiles/platform.make
 
-MVERS = "mDNSResponder-161.1"
+MVERS = "mDNSResponder-164"
 
 DDNSWRITECONFIG = "$(DSTROOT)/Library/Application Support/Bonjour/ddnswriteconfig"
 
index 2b4356d9fa3d1e10ef48101d8c587026cefe75c5..01e063ad08b061bb9b80dd5254b354ab25012d6b 100644 (file)
     Change History (most recent first):
 
 $Log: DNSCommon.c,v $
+Revision 1.185  2007/10/10 20:22:03  cheshire
+Added sanity checks in mDNSSendDNSMessage -- we've seen crashes in DNSDigest_SignMessage
+apparently caused by trying to sign zero-length messages
+
 Revision 1.184  2007/10/05 17:56:07  cheshire
 Move CountLabels and SkipLeadingLabels to DNSCommon.c so they're callable from other files
 
@@ -2495,6 +2499,12 @@ mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNS
        mDNSu16 numAdditionals = msg->h.numAdditionals;
        mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions;
 
+       if (end <= msg->data || end - msg->data > AbsoluteMaxDNSMessageData)
+               {
+               LogMsg("mDNSSendDNSMessage: invalid message %p %p %d", msg->data, end, end - msg->data);
+               return mStatus_BadParamErr;
+               }
+
        // Put all the integer values in IETF byte-order (MSB first, LSB second)
        *ptr++ = (mDNSu8)(numQuestions   >> 8);
        *ptr++ = (mDNSu8)(numQuestions   &  0xFF);
index 4b0d972317377bd2f0c2d8d20bc89700d30b9354..854c6565d8164e187cfae154c9a8af6163b6af3b 100755 (executable)
     Change History (most recent first):
 
 $Log: mDNS.c,v $
+Revision 1.751  2007/10/30 23:49:41  cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+LLQ state was not being transferred properly between duplicate questions
+
+Revision 1.750  2007/10/29 23:58:52  cheshire
+<rdar://problem/5536979> BTMM: Need to create NAT port mapping for receiving LLQ events
+Use standard "if (mDNSIPv4AddressIsOnes(....ExternalAddress))" mechanism to determine whether callback has been invoked yet
+
+Revision 1.749  2007/10/29 21:28:36  cheshire
+Change "Correcting TTL" log message to LogOperation to suppress it in customer build
+
+Revision 1.748  2007/10/29 20:02:50  cheshire
+<rdar://problem/5526813> BTMM: Wide-area records being announced via multicast
+
+Revision 1.747  2007/10/26 22:53:50  cheshire
+Made mDNS_Register_internal and mDNS_Deregister_internal use AuthRecord_uDNS macro
+instead of replicating the logic in both places
+
+Revision 1.746  2007/10/25 22:48:50  cheshire
+Added LogOperation message saying when we restart GetZoneData for record and service registrations
+
+Revision 1.745  2007/10/25 20:48:47  cheshire
+For naming consistency (with AuthRecord's UpdateServer) renamed 'ns' to 'SRSUpdateServer'
+
+Revision 1.744  2007/10/25 20:06:14  cheshire
+Don't try to do SOA queries using private DNS (TLS over TCP) queries
+
+Revision 1.743  2007/10/25 00:12:46  cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+Retrigger service registrations whenever a new network interface is added
+
+Revision 1.742  2007/10/24 22:40:06  cheshire
+Renamed: RecordRegistrationCallback          -> RecordRegistrationGotZoneData
+Renamed: ServiceRegistrationZoneDataComplete -> ServiceRegistrationGotZoneData
+
+Revision 1.741  2007/10/24 00:50:29  cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+Retrigger record registrations whenever a new network interface is added
+
+Revision 1.740  2007/10/23 00:38:03  cheshire
+When sending uDNS cache expiration query, need to increment rr->UnansweredQueries
+or code will spin sending the same cache expiration query repeatedly
+
+Revision 1.739  2007/10/22 23:46:41  cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+Need to clear question->nta pointer after calling CancelGetZoneData()
+
+Revision 1.738  2007/10/19 22:08:49  cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+Additional fixes and refinements
+
+Revision 1.737  2007/10/18 23:06:42  cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+Additional fixes and refinements
+
+Revision 1.736  2007/10/18 20:23:17  cheshire
+Moved SuspendLLQs into mDNS.c, since it's only called from one place
+
+Revision 1.735  2007/10/18 00:12:34  cheshire
+Fixed "unused variable" compiler warning
+
+Revision 1.734  2007/10/17 22:49:54  cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+
+Revision 1.733  2007/10/17 22:37:23  cheshire
+<rdar://problem/5536979> BTMM: Need to create NAT port mapping for receiving LLQ events
+
+Revision 1.732  2007/10/17 21:53:51  cheshire
+Improved debugging messages; renamed startLLQHandshakeCallback to LLQGotZoneData
+
+Revision 1.731  2007/10/17 18:37:50  cheshire
+<rdar://problem/5539930> Goodbye packets not being sent for services on shutdown
+Further refinement: pre-increment m->CurrentRecord before calling mDNS_Deregister_internal()
+
+Revision 1.730  2007/10/16 21:16:07  cheshire
+<rdar://problem/5539930> Goodbye packets not being sent for services on shutdown
+
 Revision 1.729  2007/10/05 17:56:10  cheshire
 Move CountLabels and SkipLeadingLabels to DNSCommon.c so they're callable from other files
 
@@ -751,6 +828,10 @@ mDNSexport void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q)
        if (m->mDNS_busy != m->mDNS_reentrancy+1)
                LogMsg("SetNextQueryTime: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
 
+#if ForceAlerts
+       if (m->mDNS_busy != m->mDNS_reentrancy+1) *(long*)0 = 0;
+#endif
+
        if (ActiveQuestion(q))
                {
                mDNSs32 sendtime = q->LastQTime + q->ThisQInterval;
@@ -1293,7 +1374,7 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr)
                *p = rr;
                }
 
-       if (rr->resrec.InterfaceID != mDNSInterface_Any || rr->ForceMCast || IsLocalDomain(rr->resrec.name))
+       if (!AuthRecord_uDNS(rr))
                {
                // For records that are not going to probe, acknowledge them right away
                if (rr->resrec.RecordType != kDNSRecordTypeUnique && rr->resrec.RecordType != kDNSRecordTypeDeregistering)
@@ -1307,8 +1388,7 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr)
                rr->AnnounceCount = 0;
                rr->state = regState_FetchingZoneData;
                rr->uselease = mDNStrue;
-               rr->nta = StartGetZoneData(m, rr->resrec.name, ZoneServiceUpdate, RecordRegistrationCallback, rr);
-               return rr->nta ? mStatus_NoError : mStatus_NoMemoryErr;
+               rr->nta = StartGetZoneData(m, rr->resrec.name, ZoneServiceUpdate, RecordRegistrationGotZoneData, rr);
                }
 #endif
        
@@ -1429,18 +1509,17 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr,
        // actual goodbye packets.
        
 #ifndef UNICAST_DISABLED
-       if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly && !rr->ForceMCast && !IsLocalDomain(rr->resrec.name))
-               if (rr->RequireGoodbye)
-                       {
-                       if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; }
-                       rr->resrec.RecordType    = kDNSRecordTypeDeregistering;
-                       uDNS_DeregisterRecord(m, rr);
-                       // At this point unconditionally we bail out
-                       // Either uDNS_DeregisterRecord will have completed synchronously, and called CompleteDeregistration,
-                       // which calls us back here with RequireGoodbye set to false, or it will have initiated the deregistration
-                       // process and will complete asynchronously. Either way we don't need to do anything more here.
-                       return(mStatus_NoError);
-                       }
+       if (AuthRecord_uDNS(rr) && rr->RequireGoodbye)
+               {
+               if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; }
+               rr->resrec.RecordType    = kDNSRecordTypeDeregistering;
+               uDNS_DeregisterRecord(m, rr);
+               // At this point unconditionally we bail out
+               // Either uDNS_DeregisterRecord will have completed synchronously, and called CompleteDeregistration,
+               // which calls us back here with RequireGoodbye set to false, or it will have initiated the deregistration
+               // process and will complete asynchronously. Either way we don't need to do anything more here.
+               return(mStatus_NoError);
+               }
 #endif UNICAST_DISABLED
 
        if (RecordType == kDNSRecordTypeShared && (rr->RequireGoodbye || rr->LocalAnswer))
@@ -2308,8 +2387,10 @@ mDNSlocal void SendQueries(mDNS *const m)
                                        LogOperation("Sending %d%% cache expiration query for %s", 80 + 5 * rr->UnansweredQueries, CRDisplayString(m, rr));
                                        q = rr->CRActiveQuestion;
                                        ExpireDupSuppressInfoOnInterface(q->DupSuppress, m->timenow - TicksTTL(rr)/20, rr->resrec.InterfaceID);
-                                       if      (q->Target.type) q->SendQNow = mDNSInterfaceMark;       // If targeted query, mark it
-                                       else if (!mDNSOpaque16IsZero(q->TargetQID))     q->LastQTime = m->timenow - q->ThisQInterval;   // For uDNS, adjust LastQTime
+                                       // For uDNS queries (TargetQID non-zero) we adjust LastQTime,
+                                       // and bump UnansweredQueries so that we don't spin trying to send the same cache expiration query repeatedly
+                                       if      (q->Target.type)                        q->SendQNow = mDNSInterfaceMark;        // If targeted query, mark it
+                                       else if (!mDNSOpaque16IsZero(q->TargetQID))     { q->LastQTime = m->timenow - q->ThisQInterval; rr->UnansweredQueries++; }
                                        else if (q->SendQNow == mDNSNULL)               q->SendQNow = rr->resrec.InterfaceID;
                                        else if (q->SendQNow != rr->resrec.InterfaceID) q->SendQNow = mDNSInterfaceMark;
                                        }
@@ -2426,7 +2507,7 @@ mDNSlocal void SendQueries(mDNS *const m)
                        {
                        AuthRecord *rr = m->CurrentRecord;
                        m->CurrentRecord = rr->next;
-                       if (rr->resrec.RecordType == kDNSRecordTypeUnique)                      // For all records that are still probing...
+                       if (!AuthRecord_uDNS(rr) && rr->resrec.RecordType == kDNSRecordTypeUnique)      // For all records that are still probing...
                                {
                                // 1. If it's not reached its probe time, just make sure we update m->NextScheduledProbe correctly
                                if (m->timenow - (rr->LastAPTime + rr->ThisAPInterval) < 0)
@@ -3402,6 +3483,46 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m)
        return(m->NextScheduledEvent);
        }
 
+mDNSlocal void SuspendLLQs(mDNS *m)
+       {
+       DNSQuestion *q;
+       for (q = m->Questions; q; q = q->next)
+               if (ActiveQuestion(q) && !mDNSOpaque16IsZero(q->TargetQID) && q->LongLived)
+                       {
+                       // If necessary, tell server it can delete this LLQ state
+                       if (q->state == LLQ_Established) sendLLQRefresh(m, q, 0);
+                       if (q->nta) { CancelGetZoneData(m, q->nta); q->nta = mDNSNULL; }
+                       if (q->tcp) { DisposeTCPConn(q->tcp); q->tcp = mDNSNULL; }
+                       q->state = LLQ_InitialRequest;  // Will need to set up new LLQ on wake from sleep
+                       q->id = zeroOpaque64;
+                       }
+       }
+
+mDNSlocal void ActivateUnicastQuery(mDNS *const m, DNSQuestion *const question)
+       {
+       // For now this AutoTunnel stuff is specific to Mac OS X.
+       // In the future, if there's demand, we may see if we can abstract it out cleanly into the platform layer
+#if APPLE_OSX_mDNSResponder
+       if (question->qtype == kDNSType_AAAA && question->AuthInfo && question->AuthInfo->AutoTunnel && question->QuestionCallback != AutoTunnelCallback)
+               {
+               question->NoAnswer = NoAnswer_Suspended;
+               AddNewClientTunnel(m, question);
+               return;
+               }
+#endif // APPLE_OSX_mDNSResponder
+
+       if (!question->DuplicateOf)
+               {
+               LogOperation("ActivateUnicastQuery: %##s %s%s",
+                       question->qname.c, DNSTypeName(question->qtype), question->AuthInfo ? " (Private)" : "");
+               if (question->nta) { CancelGetZoneData(m, question->nta); question->nta = mDNSNULL; }
+               if (question->LongLived) question->state = LLQ_InitialRequest;
+               question->ThisQInterval = InitialQuestionInterval;
+               question->LastQTime     = m->timenow - question->ThisQInterval;
+               SetNextQueryTime(m, question);
+               }
+       }
+
 // Call mDNSCoreMachineSleep(m, mDNStrue) when the machine is about to go to sleep.
 // Call mDNSCoreMachineSleep(m, mDNSfalse) when the machine is has just woken up.
 // Normally, the platform support layer below mDNSCore should call this, not the client layer above.
@@ -3425,7 +3546,9 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleepstate)
        if (sleepstate)
                {
 #ifndef UNICAST_DISABLED
-               uDNS_Sleep(m);
+               SuspendLLQs(m);
+               SleepServiceRegistrations(m);
+               SleepRecordRegistrations(m);
 #endif
                // Mark all the records we need to deregister and send them
                for (rr = m->ResourceRecords; rr; rr=rr->next)
@@ -3441,9 +3564,20 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleepstate)
                CacheRecord *cr;
 
 #ifndef UNICAST_DISABLED
+               // On wake, retrigger all our uDNS questions
+               if (m->CurrentQuestion)
+                       LogMsg("RestartQueries: ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
+               m->CurrentQuestion = m->Questions;
+               while (m->CurrentQuestion)
+                       {
+                       DNSQuestion *q = m->CurrentQuestion;
+                       m->CurrentQuestion = m->CurrentQuestion->next;
+                       if (!mDNSOpaque16IsZero(q->TargetQID)) ActivateUnicastQuery(m, q);
+                       }
+               // and reactivtate record (and service) registrations
                uDNS_Wake(m);
 #endif
-        // 1. Retrigger all our questions
+        // 1. Retrigger all our mDNS questions
                for (q = m->Questions; q; q=q->next)                            // Scan our list of questions
                        if (mDNSOpaque16IsZero(q->TargetQID) && ActiveQuestion(q))
                                {
@@ -3462,13 +3596,14 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleepstate)
 
                // 3. Retrigger probing and announcing for all our authoritative records
                for (rr = m->ResourceRecords; rr; rr=rr->next)
-                       {
-                       if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique;
-                       rr->ProbeCount     = DefaultProbeCountForRecordType(rr->resrec.RecordType);
-                       rr->AnnounceCount  = InitialAnnounceCount;
-                       rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
-                       InitializeLastAPTime(m, rr);
-                       }
+                       if (!AuthRecord_uDNS(rr))
+                               {
+                               if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique;
+                               rr->ProbeCount     = DefaultProbeCountForRecordType(rr->resrec.RecordType);
+                               rr->AnnounceCount  = InitialAnnounceCount;
+                               rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
+                               InitializeLastAPTime(m, rr);
+                               }
                }
 
        mDNS_Unlock(m);
@@ -4296,8 +4431,7 @@ mDNSexport void GrantCacheExtensions(mDNS *const m, DNSQuestion *q, mDNSu32 leas
 
 mDNSlocal mDNSu32 GetEffectiveTTL(const uDNS_LLQType LLQType, mDNSu32 ttl)             // TTL in seconds
        {
-       if      (LLQType == uDNS_LLQ_Poll)  ttl = LLQ_POLL_INTERVAL * 2 / mDNSPlatformOneSecond;
-       else if (LLQType == uDNS_LLQ_Setup) ttl = kLLQ_DefLease;
+       if      (LLQType == uDNS_LLQ_Entire) ttl = kLLQ_DefLease;
        else if (LLQType == uDNS_LLQ_Events)
                {
                // If the TTL is -1 for uDNS LLQ event packet, that means "remove"
@@ -4368,6 +4502,8 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m,
                response->h.numAuthorities, response->h.numAuthorities == 1 ? "y,  " : "ies,",
                response->h.numAdditionals, response->h.numAdditionals == 1 ? "" : "s", LLQType);
 
+       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
@@ -4398,9 +4534,9 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m,
                                for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
                                        if (SameNameRecordAnswersQuestion(&rr->resrec, &q))
                                                {
-                                               //LogMsg("uDNS Q for %s", CRDisplayString(m, rr));
+                                               //LogMsg("uDNS marking %s", CRDisplayString(m, rr));
                                                // Don't want to disturb rroriginalttl here, because code below might need it for the exponential backoff doubling algorithm
-                                               rr->TimeRcvd          = m->timenow - rr->resrec.rroriginalttl * mDNSPlatformOneSecond;
+                                               rr->TimeRcvd          = m->timenow - TicksTTL(rr) - 1;
                                                rr->UnansweredQueries = MaxUnansweredQueries;
                                                }
                                }
@@ -4645,9 +4781,9 @@ exit:
                                                // so our received TTLs are expected to vary in that case
                                                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 &&
+                                                       if (!(r2->resrec.rroriginalttl == 240 && r1->resrec.rroriginalttl == 60 && r2->resrec.rrtype == kDNSType_TXT) &&
                                                                mDNSOpaque16IsZero(response->h.id))
-                                                               LogMsg("Correcting TTL from %4d to %4d for %s",
+                                                               LogOperation("Correcting TTL from %4d to %4d for %s",
                                                                        r2->resrec.rroriginalttl, r1->resrec.rroriginalttl, CRDisplayString(m, r2));
                                                        r2->resrec.rroriginalttl = r1->resrec.rroriginalttl;
                                                        }
@@ -4688,19 +4824,19 @@ exit:
                ptr = getQuestion(response, ptr, end, InterfaceID, &q);
                if (ptr && ExpectingUnicastResponseForQuestion(m, response->h.id, &q))
                        {
-                       CacheRecord *rr;
+                       CacheRecord *rr, *neg = mDNSNULL;
                        mDNSu32 slot = HashSlot(&q.qname);
                        CacheGroup *cg = CacheGroupForName(m, slot, q.qnamehash, &q.qname);
                        for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
                                if (SameNameRecordAnswersQuestion(&rr->resrec, &q))
                                        {
                                        // 1. If we got a fresh answer to this query, then don't need to generate a negative entry
-                                       // 2. If we already had a negative entry which we were about to discard, then we should resurrect it
-                                       if (rr->resrec.rroriginalttl) break;
-                                       if (rr->resrec.RecordType == kDNSRecordTypePacketNegative) break;
+                                       if (rr->TimeRcvd + TicksTTL(rr) - m->timenow > 0) break;
+                                       // 2. If we already had a negative entry, keep track of it so we can resurrect it instead of creating a new one
+                                       if (rr->resrec.RecordType == kDNSRecordTypePacketNegative) neg = rr;
                                        }
 
-                       if (!rr || rr->resrec.RecordType == kDNSRecordTypePacketNegative)
+                       if (!rr)
                                {
                                // We start off assuming a negative caching TTL of 60 seconds
                                // but then look to see if we can find an SOA authority record to tell us a better value we should be using
@@ -4741,24 +4877,25 @@ exit:
                                // of 60 seconds, and we end up polling the server every minute for a record that doesn't exist.
                                // With this fix in place, when this happens, we double the effective TTL each time (up to one hour),
                                // so that we back off our polling rate and don't keep hitting the server continually.
-                               if (rr)
+                               if (neg)
                                        {
-                                       if (negttl < rr->resrec.rroriginalttl * 2)
-                                               negttl = rr->resrec.rroriginalttl * 2;
+                                       if (negttl < neg->resrec.rroriginalttl * 2)
+                                               negttl = neg->resrec.rroriginalttl * 2;
                                        if (negttl > 3600)
                                                negttl = 3600;
                                        }
 
                                negttl = GetEffectiveTTL(LLQType, negttl);      // Add 25% grace period if necessary
 
-                               if (rr) LogOperation("Renewing negative TTL from %d to %d %s", rr->resrec.rroriginalttl, negttl, CRDisplayString(m, rr));
-
                                // If we already had a negative cache entry just update it, else make one or more new negative cache entries
-                               if (rr)
-                                       RefreshCacheRecord(m, rr, negttl);
+                               if (neg)
+                                       {
+                                       LogOperation("Renewing negative TTL from %d to %d %s", neg->resrec.rroriginalttl, negttl, CRDisplayString(m, neg));
+                                       RefreshCacheRecord(m, neg, negttl);
+                                       }
                                else while (1)
                                        {
-                                       LogOperation("Making negative cache entry TTL %d for %##s (%s)", negttl, name->c, DNSTypeName(q.qtype));
+                                       LogOperation("mDNSCoreReceiveResponse making negative cache entry TTL %d for %##s (%s)", negttl, name->c, DNSTypeName(q.qtype));
                                        MakeNegativeCacheRecord(m, name, hash, q.qtype, q.qclass, negttl);
                                        CreateNewCacheEntry(m, slot, cg);
                                        m->rec.r.resrec.RecordType = 0;         // Clear RecordType to show we're not still using it
@@ -4859,13 +4996,14 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *co
        m->PktNum++;
 #ifndef UNICAST_DISABLED
        if (!dstaddr || (!mDNSAddressIsAllDNSLinkGroup(dstaddr) && (QR_OP == StdR || QR_OP == UpdR)))
-               {
-               if (!mDNSOpaque16IsZero(msg->h.id)) ifid = mDNSInterface_Any;
-               if (mDNS_LogLevel >= MDNS_LOG_VERBOSE_DEBUG)
-                       DumpPacket(m, mDNSfalse, TLS ? "TLS" : !dstaddr ? "TCP" : "UDP", srcaddr, srcport, msg, end);
-               uDNS_ReceiveMsg(m, msg, end, srcaddr, srcport);
-               // Note: mDNSCore also needs to get access to received unicast responses
-               }
+               if (!mDNSOpaque16IsZero(msg->h.id)) // uDNS_ReceiveMsg only needs to get real uDNS responses, not "QU" mDNS responses
+                       {
+                       ifid = mDNSInterface_Any;
+                       if (mDNS_LogLevel >= MDNS_LOG_VERBOSE_DEBUG)
+                               DumpPacket(m, mDNSfalse, TLS ? "TLS" : !dstaddr ? "TCP" : "UDP", srcaddr, srcport, msg, end);
+                       uDNS_ReceiveMsg(m, msg, end, srcaddr, srcport);
+                       // Note: mDNSCore also needs to get access to received unicast responses
+                       }
 #endif
        if      (QR_OP == StdQ) mDNSCoreReceiveQuery   (m, msg, end, srcaddr, srcport, dstaddr, dstport, ifid);
        else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, srcaddr, srcport, dstaddr, dstport, ifid);
@@ -4894,6 +5032,12 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *co
 #define SameQTarget(A,B) (((A)->Target.type == mDNSAddrType_None && (B)->Target.type == mDNSAddrType_None) || \
        (mDNSSameAddress(&(A)->Target, &(B)->Target) && mDNSSameIPPort((A)->TargetPort, (B)->TargetPort)))
 
+// 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
+// doing a standard DNS query for the _dns-query-tls._tcp SRV record for company.com. If we make the latter (public) query
+// a duplicate of the former (private) query, then it will block forever waiting for an answer that will never come.
+
 mDNSlocal DNSQuestion *FindDuplicateQuestion(const mDNS *const m, const DNSQuestion *const question)
        {
        DNSQuestion *q;
@@ -4904,11 +5048,11 @@ mDNSlocal DNSQuestion *FindDuplicateQuestion(const mDNS *const m, const DNSQuest
        for (q = m->Questions; q && q != question; q=q->next)           // Scan our list for another question
                if (q->InterfaceID == question->InterfaceID &&                  // with the same InterfaceID,
                        SameQTarget(q, question)                &&                      // and same unicast/multicast target settings
-                       q->qtype       == question->qtype       &&                      // type,
-                       q->qclass      == question->qclass      &&                      // class,
-                       q->AuthInfo    == question->AuthInfo    &&                      // and privacy status matches
-                       q->LongLived   == question->LongLived   &&                      // and long-lived status matches
-                       q->qnamehash   == question->qnamehash   &&
+                       q->qtype      == question->qtype        &&                      // type,
+                       q->qclass     == question->qclass       &&                      // class,
+                       q->LongLived  == question->LongLived    &&                      // and long-lived status matches
+                       (!q->AuthInfo || question->AuthInfo)    &&                      // to avoid deadlock, don't make public query dup of a private one
+                       q->qnamehash  == question->qnamehash    &&
                        SameDomainName(&q->qname, &question->qname))            // and name
                        return(q);
        return(mDNSNULL);
@@ -4938,8 +5082,6 @@ mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, DNSQuestion *const questi
                                q->servPort          = question->servPort;
 
                                q->state             = question->state;
-                       //      q->NATInfoUDP        = question->NATInfoUDP;
-                               q->eventPort         = question->eventPort;
                        //      q->tcp               = question->tcp;
                                q->origLease         = question->origLease;
                                q->expire            = question->expire;
@@ -4947,7 +5089,6 @@ mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, DNSQuestion *const questi
                                q->id                = question->id;
 
                                question->nta        = mDNSNULL;        // If we've got a GetZoneData in progress, transfer it to the newly active question
-                       //      question->NATInfoUDP = mDNSNULL;
                        //      question->tcp        = mDNSNULL;
                                if (q->nta)
                                        {
@@ -4957,7 +5098,13 @@ mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, DNSQuestion *const questi
 
                                // Need to work out how to safely transfer this state too -- appropriate context pointers need to be updated or the code will crash
                                if (question->tcp) LogOperation("UpdateQuestionDuplicates did not transfer tcp pointer");
-       
+
+                               if (question->state == LLQ_Established)
+                                       {
+                                       LogOperation("UpdateQuestionDuplicates transferred LLQ state for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+                                       question->state = 0;    // Must zero question->state, or mDNS_StopQuery_internal will clean up and cancel our LLQ from the server
+                                       }
+
                                SetNextQueryTime(m,q);
                                }
        }
@@ -4981,38 +5128,17 @@ mDNSlocal DNSServer *GetServerForName(mDNS *m, const domainname *name)
 #define ValidQuestionTarget(Q) (((Q)->Target.type == mDNSAddrType_IPv4 || (Q)->Target.type == mDNSAddrType_IPv6) && \
        (mDNSSameIPPort((Q)->TargetPort, UnicastDNSPort) || mDNSSameIPPort((Q)->TargetPort, MulticastDNSPort)))
 
-mDNSlocal void ActivateUnicastQuery(mDNS *const m, DNSQuestion *const question)
+// Called in normal client context (lock not held)
+mDNSlocal void LLQNATCallback(mDNS *m, NATTraversalInfo *n)
        {
-       // For now this AutoTunnel stuff is specific to Mac OS X.
-       // In the future, if there's demand, we may see if we can abstract it out cleanly into the platform layer
-#if APPLE_OSX_mDNSResponder
-       if (question->qtype == kDNSType_AAAA && question->AuthInfo && question->AuthInfo->AutoTunnel && question->QuestionCallback != AutoTunnelCallback)
-               {
-               question->NoAnswer = NoAnswer_Suspended;
-               AddNewClientTunnel(m, question);
-               return;
-               }
-#endif // APPLE_OSX_mDNSResponder
-
-       if (!question->DuplicateOf)
-               {
-               if (question->LongLived)
-                       {
-                       question->ThisQInterval = 0;    // Question is suspended, waiting for GetZoneData to complete
-                       question->LastQTime     = m->timenow;
-                       LogOperation("uDNS_InitLongLivedQuery: %##s %s %s %d",
-                               question->qname.c, DNSTypeName(question->qtype), question->AuthInfo ? "(Private)" : "", question->ThisQInterval);
-                       if (question->nta) CancelGetZoneData(m, question->nta);
-                       question->state = LLQ_GetZoneInfo;              // Necessary to stop "bad state" error in startLLQHandshakeCallback
-                       question->nta = StartGetZoneData(m, &question->qname, ZoneServiceLLQ, startLLQHandshakeCallback, question);
-                       if (!question->nta) LogMsg("ERROR: startLLQ - StartGetZoneData failed");
-                       }
-               else
-                       {
-                       question->ThisQInterval = InitialQuestionInterval;
-                       question->LastQTime     = m->timenow - question->ThisQInterval;
-                       }
-               }
+       DNSQuestion *q;
+       (void)n;    // Unused
+       mDNS_Lock(m);
+       LogOperation("LLQNATCallback external address:port %.4a:%u", &n->ExternalAddress, mDNSVal16(n->ExternalPort));
+       for (q = m->Questions; q; q=q->next)
+               if (ActiveQuestion(q) && !mDNSOpaque16IsZero(q->TargetQID) && q->LongLived)
+                       startLLQHandshake(m, q);        // If ExternalPort is zero, will do StartLLQPolling instead
+       mDNS_Unlock(m);
        }
 
 mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question)
@@ -5099,13 +5225,7 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu
                question->UniqueAnswers     = 0;
                question->FlappingInterface1 = mDNSNULL;
                question->FlappingInterface2 = mDNSNULL;
-               // GetZoneData queries are a special case -- even if we have a key for them, we don't do them privately,
-               // because that would result in an infinite loop (i.e. to do a private query we first need to get
-               // the _dns-query-tls SRV record for the zone, and we can't do *that* privately because to do so
-               // we'd need to already know the _dns-query-tls SRV record.
-               // Also: Make sure we set AuthInfo before calling FindDuplicateQuestion()
-               question->AuthInfo          = (question->QuestionCallback == GetZoneData_QuestionCallback) ? mDNSNULL
-                                           : GetAuthInfoForName_internal(m, &question->qname);
+               question->AuthInfo          = GetAuthInfoForQuestion(m, question);              // Must do this before calling FindDuplicateQuestion()
                question->DuplicateOf       = FindDuplicateQuestion(m, question);
                question->NextInDQList      = mDNSNULL;
                question->SendQNow          = mDNSNULL;
@@ -5121,14 +5241,14 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu
                question->tcp               = mDNSNULL;
                question->NoAnswer          = NoAnswer_Normal;
 
-               question->state             = LLQ_GetZoneInfo;
-               mDNSPlatformMemZero(&question->NATInfoUDP, sizeof(question->NATInfoUDP));
-               question->eventPort         = zeroIPPort;
+               question->state             = LLQ_InitialRequest;
                question->origLease         = 0;
                question->expire            = 0;
                question->ntries            = 0;
                question->id                = zeroOpaque64;
 
+               if (question->DuplicateOf) question->AuthInfo = question->DuplicateOf->AuthInfo;
+
                for (i=0; i<DupSuppressInfoSize; i++)
                        question->DupSuppress[i].InterfaceID = mDNSNULL;
 
@@ -5159,6 +5279,17 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu
                                {
                                question->qDNSServer = GetServerForName(m, &question->qname);
                                ActivateUnicastQuery(m, question);
+
+                               // If long-lived query, and we don't have our NAT mapping active, start it now
+                               if (question->LongLived && !m->LLQNAT.clientContext)
+                                       {
+                                       m->LLQNAT.Protocol       = NATOp_MapUDP;
+                                       m->LLQNAT.IntPort        = m->UnicastPort4;
+                                       m->LLQNAT.RequestedPort  = m->UnicastPort4;
+                                       m->LLQNAT.clientCallback = LLQNATCallback;
+                                       m->LLQNAT.clientContext  = (void*)1; // Means LLQ NAT Traversal is active
+                                       mDNS_StartNATOperation_internal(m, &m->LLQNAT);
+                                       }
                                }
                        SetNextQueryTime(m,question);
                        }
@@ -5189,7 +5320,9 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que
        if (*q) *q = (*q)->next;
        else
                {
+#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
@@ -5247,9 +5380,29 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que
        // so if we delete it earlier in this routine, we could find that our "question->next" pointer above is already
        // invalid before we even use it. By making sure that we update m->CurrentQuestion and m->NewQuestions if necessary
        // *first*, then they're all ready to be updated a second time if necessary when we cancel our GetZoneData query.
-       if (question->nta) CancelGetZoneData(m, question->nta);
+       if (question->nta) { CancelGetZoneData(m, question->nta); question->nta = mDNSNULL; }
        if (question->tcp) { DisposeTCPConn(question->tcp); question->tcp = mDNSNULL; }
-       if (!mDNSOpaque16IsZero(question->TargetQID) && question->LongLived) uDNS_StopLongLivedQuery(m, question);
+       if (!mDNSOpaque16IsZero(question->TargetQID) && question->LongLived)
+               {
+               // Scan our list to see if any more wide-area LLQs remain. If not, stop our NAT Traversal.
+               DNSQuestion *q;
+               for (q = m->Questions; q; q=q->next)
+                       if (!mDNSOpaque16IsZero(q->TargetQID) && q->LongLived) break;
+               if (!q)
+                       {
+                       if (!m->LLQNAT.clientContext)           // Should never happen, but just in case...
+                               LogMsg("mDNS_StopQuery ERROR LLQNAT.clientContext NULL");
+                       else
+                               {
+                               LogOperation("Stopping LLQNAT");
+                               mDNS_StopNATOperation_internal(m, &m->LLQNAT);
+                               m->LLQNAT.clientContext = mDNSNULL; // Means LLQ NAT Traversal not running
+                               }
+                       }
+
+               // If necessary, tell server it can delete this LLQ state
+               if (question->state == LLQ_Established) sendLLQRefresh(m, question, 0);
+               }
 
        return(mStatus_NoError);
        }
@@ -5344,7 +5497,7 @@ mDNSexport mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question,
        if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) return(mStatus_BadParamErr);
 
 #ifndef UNICAST_DISABLED
-    if (question->InterfaceID != mDNSInterface_LocalOnly && !question->ForceMCast && !IsLocalDomain(&question->qname))
+    if (Question_uDNS(question))
                {
                question->LongLived     = mDNStrue;
                question->ThisQInterval = InitialQuestionInterval;
@@ -5899,6 +6052,8 @@ mDNSlocal void UpdateInterfaceProtocols(mDNS *const m, NetworkInterfaceInfo *act
 
 mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping)
        {
+       AuthRecord *rr;
+       ServiceRecordSet *s;
        mDNSBool FirstOfType = mDNStrue;
        NetworkInterfaceInfo **p = &m->HostInterfaces;
 
@@ -5955,7 +6110,6 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s
        if (set->McastTxRx && ((m->KnownBugs & mDNS_KnownBug_PhantomInterfaces) || FirstOfType || set->InterfaceActive))
                {
                DNSQuestion *q;
-               AuthRecord *rr;
                // If flapping, delay between first and second queries is eight seconds instead of one
                mDNSs32 delay    = flapping ? mDNSPlatformOneSecond   * 5 : 0;
                mDNSu8  announce = flapping ? (mDNSu8)1                   : InitialAnnounceCount;
@@ -5996,14 +6150,30 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s
                // For all our non-specific authoritative resource records (and any dormant records specific to this interface)
                // we now need them to re-probe if necessary, and then re-announce.
                for (rr = m->ResourceRecords; rr; rr=rr->next)
-                       if (!rr->resrec.InterfaceID || rr->resrec.InterfaceID == set->InterfaceID)
-                               {
-                               if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique;
-                               rr->ProbeCount     = DefaultProbeCountForRecordType(rr->resrec.RecordType);
-                               if (rr->AnnounceCount < announce) rr->AnnounceCount  = announce;
-                               rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
-                               InitializeLastAPTime(m, rr);
-                               }
+                       if (!AuthRecord_uDNS(rr))
+                               if (!rr->resrec.InterfaceID || rr->resrec.InterfaceID == set->InterfaceID)
+                                       {
+                                       if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique;
+                                       rr->ProbeCount     = DefaultProbeCountForRecordType(rr->resrec.RecordType);
+                                       if (rr->AnnounceCount < announce) rr->AnnounceCount  = announce;
+                                       rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
+                                       InitializeLastAPTime(m, rr);
+                                       }
+               }
+
+       for (rr = m->ResourceRecords; rr; rr=rr->next)
+               if (AuthRecord_uDNS(rr))
+                       {
+                       LogOperation("mDNS_RegisterInterface: StartGetZoneData for %##s", rr->resrec.name->c);
+                       if (rr->nta) CancelGetZoneData(m, rr->nta);
+                       rr->nta = StartGetZoneData(m, rr->resrec.name, ZoneServiceUpdate, RecordRegistrationGotZoneData, rr);
+                       }
+
+       for (s = m->ServiceRegistrations; s; s = s->uDNS_next)
+               {
+               LogOperation("mDNS_RegisterInterface: StartGetZoneData for %##s", s->RR_SRV.resrec.name->c);
+               if (s->nta) CancelGetZoneData(m, s->nta);
+               s->nta = StartGetZoneData(m, s->RR_SRV.resrec.name, ZoneServiceUpdate, ServiceRegistrationGotZoneData, s);
                }
 
        mDNS_Unlock(m);
@@ -6218,14 +6388,14 @@ mDNSlocal mStatus uDNS_RegisterService(mDNS *const m, ServiceRecordSet *srs)
        if (!GetServiceTarget(m, srs))
                {
                // defer registration until we've got a target
-               debugf("uDNS_RegisterService - no target for %##s", srs->RR_SRV.resrec.name->c);
+               LogOperation("uDNS_RegisterService - no target for %##s", srs->RR_SRV.resrec.name->c);
                srs->state = regState_NoTarget;
                srs->nta   = mDNSNULL;
                return mStatus_NoError;
                }
 
        srs->state = regState_FetchingZoneData;
-       srs->nta = StartGetZoneData(m, srs->RR_SRV.resrec.name, ZoneServiceUpdate, ServiceRegistrationZoneDataComplete, srs);
+       srs->nta = StartGetZoneData(m, srs->RR_SRV.resrec.name, ZoneServiceUpdate, ServiceRegistrationGotZoneData, srs);
        return srs->nta ? mStatus_NoError : mStatus_NoMemoryErr;
        }
 
@@ -6249,12 +6419,11 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr,
 
        sr->state                  = regState_Zero;
        sr->srs_uselease           = 0;
-       sr->expire                 = 0;
        sr->TestForSelfConflict    = 0;
        sr->Private                = 0;
        sr->id                     = zeroID;
        sr->zone.c[0]              = 0;
-       sr->ns                     = zeroAddr;
+       sr->SRSUpdateServer        = zeroAddr;
        sr->SRSUpdatePort          = zeroIPPort;
        mDNSPlatformMemZero(&sr->NATinfo, sizeof(sr->NATinfo));
        sr->ClientCallbackDeferred = 0;
@@ -6412,7 +6581,7 @@ mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr,
                {
                *e = extra;
 #ifndef UNICAST_DISABLED
-               if (sr->RR_SRV.resrec.InterfaceID != mDNSInterface_LocalOnly && !IsLocalDomain(sr->RR_SRV.resrec.name))
+               if (AuthRecord_uDNS(&sr->RR_SRV))
                        {
                        extra->r.resrec.RecordType = kDNSRecordTypeShared;      // don't want it to conflict with the service name (???)
                        extra->r.RecordCallback = DummyCallback;        // don't generate callbacks for extra RRs for unicast services (WHY NOT????)
@@ -6792,6 +6961,7 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m)
        mDNSAddr     v4, v6, r;
        domainname   fqdn;
        DNSServer   *ptr, **p = &m->DNSServers;
+       const DNSServer *oldServers = m->DNSServers;
        DNSQuestion *q;
 
        if (m->RegisterSearchDomains) uDNS_RegisterSearchDomains(m);
@@ -6810,12 +6980,13 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m)
                if (!mDNSOpaque16IsZero(q->TargetQID))
                        {
                        DNSServer *s = GetServerForName(m, &q->qname);
-                       if (q->qDNSServer != s)
+                       DNSServer *t = q->qDNSServer;
+                       if (t != s)
                                {
                                // If DNS Server for this question has changed, reactivate it
-                               LogOperation("Updating DNS Server from %#a:%d to %#a:%d for %##s (%s)",
-                                       q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort),
-                                       s             ? &s->addr             : mDNSNULL, mDNSVal16(s             ? s->port             : zeroIPPort),
+                               LogOperation("Updating DNS Server from %p %#a:%d (%##s) to %p %#a:%d (%##s) for %##s (%s)",
+                                       t, t ? &t->addr : mDNSNULL, mDNSVal16(t ? t->port : zeroIPPort), t ? t->domain.c : (mDNSu8*)"",
+                                       s, s ? &s->addr : mDNSNULL, mDNSVal16(s ? s->port : zeroIPPort), s ? s->domain.c : (mDNSu8*)"",
                                        q->qname.c, DNSTypeName(q->qtype));
                                q->qDNSServer = s;
                                ActivateUnicastQuery(m, q);
@@ -6841,10 +7012,17 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m)
                        p = &(*p)->next;
                }
 
-       // If we have no DNS servers at all, then immediately purge all unicast cache records (including for LLQs)
-       // This is important for giving prompt remove events when the user disconnects the Ethernet cable or turns off wireless
-       // Otherwise, stale data lingers for 5-10 seconds, which is not the user-experience people expect from Bonjour
-       if (!m->DNSServers) FORALL_CACHERECORDS(slot, cg, cr) if (!cr->resrec.InterfaceID) mDNS_PurgeCacheResourceRecord(m, cr);
+       // If we now have no DNS servers at all and we used to have some, then immediately purge all unicast cache records (including for LLQs).
+       // This is important for giving prompt remove events when the user disconnects the Ethernet cable or turns off wireless.
+       // Otherwise, stale data lingers for 5-10 seconds, which is not the user-experience people expect from Bonjour.
+       // Similarly, if we now have some DNS servers and we used to have none, we want to purge any fake negative results we may have generated.
+       if ((m->DNSServers != mDNSNULL) != (oldServers != mDNSNULL))
+               {
+               int count = 0;
+               FORALL_CACHERECORDS(slot, cg, cr) if (!cr->resrec.InterfaceID) { mDNS_PurgeCacheResourceRecord(m, cr); count++; }
+               LogOperation("uDNS_SetupDNSConfig: %s available; purged %d unicast DNS records from cache",
+                       m->DNSServers ? "DNS server became" : "No DNS servers", count);
+               }
 
        // Did our FQDN change?
        if (!SameDomainName(&fqdn, &m->FQDN))
@@ -6907,7 +7085,8 @@ mDNSexport void mDNS_Close(mDNS *const m)
        m->mDNS_shutdown = mDNStrue;
 
 #ifndef UNICAST_DISABLED
-       uDNS_Sleep(m);
+       SuspendLLQs(m);
+       SleepServiceRegistrations(m);
        while (m->Hostnames) mDNS_RemoveDynDNSHostName(m, &m->Hostnames->fqdn);
 #endif
 
@@ -6963,24 +7142,23 @@ mDNSexport void mDNS_Close(mDNS *const m)
        while (m->CurrentRecord)
                {
                rr = m->CurrentRecord;
+               m->CurrentRecord = rr->next;
                if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
                        mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
-               else
-                       m->CurrentRecord = rr->next;
                }
 
        // Now deregister any remaining records we didn't get the first time through
+       m->CurrentRecord = m->ResourceRecords;
        while (m->CurrentRecord)
                {
                rr = m->CurrentRecord;
+               m->CurrentRecord = rr->next;
                if (rr->resrec.RecordType != kDNSRecordTypeDeregistering)
                        mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
-               else
-                       m->CurrentRecord = rr->next;
                }
 
-       if (m->ResourceRecords) debugf("mDNS_Close: Sending final packets for deregistering records");
-       else debugf("mDNS_Close: No deregistering records remain");
+       if (m->ResourceRecords) LogOperation("mDNS_Close: Sending final packets for deregistering records");
+       else                    LogOperation("mDNS_Close: No deregistering records remain");
 
        // If any deregistering records remain, send their deregistration announcements before we exit
        if (m->mDNSPlatformStatus != mStatus_NoError) DiscardDeregistrations(m);
index c761645db2079ad3dc2dc542316a0814e0642cb3..8673da9c4f58da4657dee7358c1f42c4f922ef84 100755 (executable)
     Change History (most recent first):
 
 $Log: mDNSEmbeddedAPI.h,v $
+Revision 1.453  2007/10/29 23:51:22  cheshire
+Added comment about NATTraversalInfo ExternalAddress field
+
+Revision 1.452  2007/10/29 18:13:40  cheshire
+Added Question_uDNS macro, analogous to AuthRecord_uDNS macro
+
+Revision 1.451  2007/10/26 23:42:57  cheshire
+Removed unused "mDNSs32 expire" field from ServiceRecordSet_struct
+
+Revision 1.450  2007/10/26 22:24:08  cheshire
+Added AuthRecord_uDNS() macro to determine when a given AuthRecord needs to be registered via unicast DNS
+
+Revision 1.449  2007/10/25 20:48:47  cheshire
+For naming consistency (with AuthRecord's UpdateServer) renamed 'ns' to 'SRSUpdateServer'
+
+Revision 1.448  2007/10/22 22:19:44  cheshire
+Tidied up code alignment
+
+Revision 1.447  2007/10/22 19:40:30  cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+Made subroutine mDNSPlatformSourceAddrForDest(mDNSAddr *const src, const mDNSAddr *const dst)
+
+Revision 1.446  2007/10/17 22:49:54  cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+
+Revision 1.445  2007/10/17 22:37:23  cheshire
+<rdar://problem/5536979> BTMM: Need to create NAT port mapping for receiving LLQ events
+
 Revision 1.444  2007/09/29 03:14:52  cheshire
 <rdar://problem/5513168> BTMM: mDNSResponder memory corruption in GetAuthInfoForName_internal
 Added AutoTunnelUnregistered macro to check state of DomainAuthInfo AuthRecords
@@ -1139,12 +1167,12 @@ typedef enum
 
 enum
        {
-       NATErr_None = 0,
-       NATErr_Vers = 1,
+       NATErr_None    = 0,
+       NATErr_Vers    = 1,
        NATErr_Refused = 2,
        NATErr_NetFail = 3,
-       NATErr_Res = 4,
-       NATErr_Opcode = 5
+       NATErr_Res     = 4,
+       NATErr_Opcode  = 5
        };
 
 typedef mDNSu16 NATErr_t;
@@ -1243,7 +1271,7 @@ struct NATTraversalInfo_struct
        // mapping response from the NAT-PMP or UPnP gateway. For example, if we ask for port 80, and
        // get assigned port 81, then thereafter we'll contine asking for port 81.
        mDNSInterfaceID             InterfaceID;
-       mDNSv4Addr                  ExternalAddress;
+       mDNSv4Addr                  ExternalAddress;    // Initially set to onesIPv4Addr, until first callback
        mDNSIPPort                  ExternalPort;
        mDNSu32                     Lifetime;
        mStatus                     Result;
@@ -1392,6 +1420,9 @@ struct AuthRecord_struct
        // DO NOT ADD ANY MORE FIELDS HERE
        };
 
+#define AuthRecord_uDNS(R) ((R)->resrec.InterfaceID == mDNSInterface_Any && !(R)->ForceMCast && !IsLocalDomain((R)->resrec.name))
+#define Question_uDNS(Q)   ((Q)->InterfaceID == mDNSInterface_Any        && !(Q)->ForceMCast && !IsLocalDomain(&(Q)->qname))
+
 // Wrapper struct for Auth Records for higher-level code that cannot use the AuthRecord's ->next pointer field
 typedef struct ARListElem
        {
@@ -1550,15 +1581,14 @@ struct ServiceRecordSet_struct
        ServiceRecordSet *uDNS_next;
        regState_t        state;
        mDNSBool          srs_uselease;                         // dynamic update contains (should contain) lease option
-       mDNSs32           expire;                                       // expiration of lease (-1 for static)
        mDNSBool          TestForSelfConflict;          // on name conflict, check if we're just seeing our own orphaned records
        mDNSBool          Private;                                      // If zone is private, DNS updates may have to be encrypted to prevent eavesdropping
        ZoneData         *nta;
        mDNSOpaque16      id;
        domainname        zone;                                         // the zone that is updated
-       mDNSAddr          ns;                                           // primary name server for the record's zone  !!!KRS not technically correct to cache longer than TTL
+       mDNSAddr          SRSUpdateServer;                      // primary name server for the record's zone  !!!KRS not technically correct to cache longer than TTL
        mDNSIPPort        SRSUpdatePort;                        // port on which server accepts dynamic updates
-       NATTraversalInfo  NATinfo;                                      // may be NULL
+       NATTraversalInfo  NATinfo;
        mDNSBool          ClientCallbackDeferred;       // invoke client callback on completion of pending operation(s)
        mStatus           DeferredStatus;                       // status to deliver when above flag is set
        mDNSBool          SRVUpdateDeferred;            // do we need to change target or port once current operation completes?
@@ -1602,24 +1632,10 @@ typedef struct
 
 typedef enum
        {
-       // Setup states
-       LLQ_UnInit            = 0,
-       LLQ_GetZoneInfo       = 1,
-       LLQ_InitialRequest    = 2,
-       LLQ_SecondaryRequest  = 3,
-       LLQ_Refresh           = 4,
-       LLQ_Retry             = 5,
-       LLQ_Established       = 6,
-       LLQ_Suspended         = 7,
-       LLQ_SuspendDeferred   = 8, // suspend once we get zone info
-       LLQ_SuspendedPoll     = 9, // suspended from polling state
-       LLQ_NatMapWaitUDP     = 10,
-
-       // Established/error states
-       LLQ_Static            = 16,
-       LLQ_Poll              = 17,
-       LLQ_Error             = 18,
-       LLQ_Cancelled         = 19
+       LLQ_InitialRequest    = 1,
+       LLQ_SecondaryRequest  = 2,
+       LLQ_Established       = 3,
+       LLQ_Poll              = 4
        } LLQ_State;
 
 // LLQ constants
@@ -1629,7 +1645,6 @@ typedef enum
 #define kLLQ_DefLease  7200 // 2 hours
 #define kLLQ_MAX_TRIES 3    // retry an operation 3 times max
 #define kLLQ_INIT_RESEND 2 // resend an un-ack'd packet after 2 seconds, then double for each additional
-#define kLLQ_DEF_RETRY 1800 // retry a failed operation after 30 minutes
 // LLQ Operation Codes
 #define kLLQOp_Setup     1
 #define kLLQOp_Refresh   2
@@ -1641,12 +1656,12 @@ typedef enum
 // LLQ Errror Codes
 enum
        {
-       LLQErr_NoError = 0,
-       LLQErr_ServFull = 1,
-       LLQErr_Static = 2,
-       LLQErr_FormErr = 3,
-       LLQErr_NoSuchLLQ = 4,
-       LLQErr_BadVers = 5,
+       LLQErr_NoError    = 0,
+       LLQErr_ServFull   = 1,
+       LLQErr_Static     = 2,
+       LLQErr_FormErr    = 3,
+       LLQErr_NoSuchLLQ  = 4,
+       LLQErr_BadVers    = 5,
        LLQErr_UnknownErr = 6
        };
 
@@ -1723,8 +1738,6 @@ struct DNSQuestion_struct
 
        // LLQ-specific fields. These fields are only meaningful when LongLived flag is set
        LLQ_State             state;
-       NATTraversalInfo      NATInfoUDP;
-       mDNSIPPort            eventPort;                // This is non-zero if this is a private LLQ. If we're behind NAT, it's the external UDP port.
        mDNSu32               origLease;                // seconds (relative)
        mDNSs32               expire;                   // ticks (absolute)
        mDNSs16               ntries;
@@ -1943,6 +1956,7 @@ struct mDNS_struct
        mDNSBool          RegisterSearchDomains;
 
        // NAT traversal fields
+       NATTraversalInfo  LLQNAT;                                       // Single shared NAT Traversal to receive inbound LLQ notifications
        NATTraversalInfo *NATTraversals;
        NATTraversalInfo *CurrentNATTraversal;
        mDNSs32           retryIntervalGetAddr;         // delta between time sent and retry
@@ -2407,10 +2421,6 @@ extern mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info,
 // Host domains added prior to specification of the primary interface address and computer name will be deferred until
 // these values are initialized.
 
-// When routable V4 interfaces are added or removed, mDNS_UpdateLLQs should be called to re-estabish LLQs in case the
-// destination address for events (i.e. the route) has changed.  For performance reasons, the caller is responsible for
-// batching changes, e.g.  calling the routine only once if multiple interfaces are simultanously removed or added.
-
 // DNS servers used to resolve unicast queries are specified by mDNS_AddDNSServer.
 // For "split" DNS configurations, in which queries for different domains are sent to different servers (e.g. VPN and external),
 // a domain may be associated with a DNS server.  For standard configurations, specify the root label (".") or NULL.
@@ -2418,7 +2428,6 @@ extern mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info,
 extern void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext);
 extern void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn);
 extern void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr,  const mDNSAddr *v6addr, const mDNSAddr *router);
-extern void mDNS_UpdateLLQs(mDNS *m);
 extern DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port);
 extern void mDNS_AddSearchDomain(const domainname *const domain);
 
@@ -2541,6 +2550,7 @@ extern long       mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long
 extern long       mDNSPlatformWriteTCP(TCPSocket *sock, const char *msg, unsigned long len);
 extern UDPSocket *mDNSPlatformUDPSocket(mDNS *const m, mDNSIPPort port);
 extern void       mDNSPlatformUDPClose(UDPSocket *sock);
+extern void       mDNSPlatformSourceAddrForDest(mDNSAddr *const src, const mDNSAddr *const dst);
 
 // mDNSPlatformTLSSetupCerts/mDNSPlatformTLSTearDownCerts used by dnsextd
 extern mStatus    mDNSPlatformTLSSetupCerts(void);
@@ -2660,7 +2670,7 @@ struct CompileTimeAssertionChecks_mDNS
        char sizecheck_AuthRecord          [(sizeof(AuthRecord)           <=  1290) ? 1 : -1];
        char sizecheck_CacheRecord         [(sizeof(CacheRecord)          <=   170) ? 1 : -1];
        char sizecheck_CacheGroup          [(sizeof(CacheGroup)           <=   170) ? 1 : -1];
-       char sizecheck_DNSQuestion         [(sizeof(DNSQuestion)          <=   700) ? 1 : -1];
+       char sizecheck_DNSQuestion         [(sizeof(DNSQuestion)          <=   560) ? 1 : -1];
        char sizecheck_ZoneData            [(sizeof(ZoneData)             <=  1600) ? 1 : -1];
        char sizecheck_NATTraversalInfo    [(sizeof(NATTraversalInfo)     <=   140) ? 1 : -1];
        char sizecheck_HostnameInfo        [(sizeof(HostnameInfo)         <=  3000) ? 1 : -1];
index 9db4e9cb768bd8685ea3f08fe4d235de215036cd..d6fddfa1b4a4e8b439c0652c398d6129aa0b47c4 100755 (executable)
        Change History (most recent first):
 
 $Log: uDNS.c,v $
+Revision 1.528  2007/11/02 21:32:30  cheshire
+<rdar://problem/5575593> BTMM: Deferring deregistration of record log messages on sleep/wake
+
+Revision 1.527  2007/11/01 16:08:51  cheshire
+Tidy up alignment of "SetRecordRetry refresh" log messages
+
+Revision 1.526  2007/10/31 19:26:55  cheshire
+Don't need to log "Permanently abandoning service registration" message when we're intentionally deleting a service
+
+Revision 1.525  2007/10/30 23:58:59  cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+After failure, double retry interval up to maximum of 30 minutes
+
+Revision 1.524  2007/10/30 20:10:47  cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+
+Revision 1.523  2007/10/30 00:54:31  cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+Fixed timing logic to double retry interval properly
+
+Revision 1.522  2007/10/30 00:04:43  cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+Made the code not give up and abandon the record when it gets an error in regState_UpdatePending state
+
+Revision 1.521  2007/10/29 23:58:52  cheshire
+<rdar://problem/5536979> BTMM: Need to create NAT port mapping for receiving LLQ events
+Use standard "if (mDNSIPv4AddressIsOnes(....ExternalAddress))" mechanism to determine whether callback has been invoked yet
+
+Revision 1.520  2007/10/29 21:48:36  cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+Added 10% random variation on LLQ renewal time, to reduce unintended timing correlation between multiple machines
+
+Revision 1.519  2007/10/29 21:37:00  cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+Added 10% random variation on record refresh time, to reduce accidental timing correlation between multiple machines
+
+Revision 1.518  2007/10/26 23:41:29  cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+
+Revision 1.517  2007/10/25 23:30:12  cheshire
+Private DNS registered records now deregistered on sleep and re-registered on wake
+
+Revision 1.516  2007/10/25 22:53:52  cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+Don't unlinkSRS and permanently give up at the first sign of trouble
+
+Revision 1.515  2007/10/25 21:08:07  cheshire
+Don't try to send record registrations/deletions before we have our server address
+
+Revision 1.514  2007/10/25 20:48:47  cheshire
+For naming consistency (with AuthRecord's UpdateServer) renamed 'ns' to 'SRSUpdateServer'
+
+Revision 1.513  2007/10/25 20:06:13  cheshire
+Don't try to do SOA queries using private DNS (TLS over TCP) queries
+
+Revision 1.512  2007/10/25 18:25:15  cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+Don't need a NAT mapping for autotunnel services
+
+Revision 1.511  2007/10/25 00:16:23  cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+Fixed retry timing logic; when DNS server returns an error code, we should retry later,
+instead of just deleting our record ("UnlinkAuthRecord") and completely giving up
+
+Revision 1.510  2007/10/24 22:40:06  cheshire
+Renamed: RecordRegistrationCallback          -> RecordRegistrationGotZoneData
+Renamed: ServiceRegistrationZoneDataComplete -> ServiceRegistrationGotZoneData
+
+Revision 1.509  2007/10/24 00:54:07  cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+
+Revision 1.508  2007/10/24 00:05:03  cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+When sending TLS/TCP LLQ setup request over VPN, need to set EventPort to 5353, not zero
+
+Revision 1.507  2007/10/23 00:33:36  cheshire
+Improved debugging messages
+
+Revision 1.506  2007/10/22 19:54:13  cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+Only put EventPort in LLQ request when sending from an RFC 1918 source address, not when sending over VPN
+
+Revision 1.505  2007/10/19 22:08:49  cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+Additional fixes and refinements
+
+Revision 1.504  2007/10/18 23:06:43  cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+Additional fixes and refinements
+
+Revision 1.503  2007/10/18 20:23:17  cheshire
+Moved SuspendLLQs into mDNS.c, since it's only called from one place
+
+Revision 1.502  2007/10/17 22:49:54  cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+
+Revision 1.501  2007/10/17 22:37:23  cheshire
+<rdar://problem/5536979> BTMM: Need to create NAT port mapping for receiving LLQ events
+
+Revision 1.500  2007/10/17 21:53:51  cheshire
+Improved debugging messages; renamed startLLQHandshakeCallback to LLQGotZoneData
+
+Revision 1.499  2007/10/16 21:16:50  cheshire
+Get rid of unused uDNS_Sleep() routine
+
+Revision 1.498  2007/10/16 20:59:41  cheshire
+Export SuspendLLQs/SleepServiceRegistrations/SleepRecordRegistrations so they're callable from other files
+
+Revision 1.497  2007/10/05 18:09:44  cheshire
+<rdar://problem/5524841> Services advertised with wrong target host
+
 Revision 1.496  2007/10/04 22:38:59  cheshire
 Added LogOperation message showing new q->ThisQInterval after sending uDNS query packet
 
 Revision 1.495  2007/10/03 00:16:19  cheshire
-In startPrivateQueryCallback, need to grab lock before calling SetNextQueryTime
+In PrivateQueryGotZoneData, need to grab lock before calling SetNextQueryTime
 
 Revision 1.494  2007/10/02 21:11:08  cheshire
 <rdar://problem/5518270> LLQ refreshes don't work, which breaks BTMM browsing
@@ -156,7 +267,7 @@ otherwise those records will expire and vanish from the cache.
 
 Revision 1.456  2007/09/05 21:00:17  cheshire
 <rdar://problem/5457287> mDNSResponder taking up 100% CPU in ReissueBlockedQuestions
-Additional refinement: ThisQInterval needs to be restored in tcpCallback, not in startPrivateQueryCallback
+Additional refinement: ThisQInterval needs to be restored in tcpCallback, not in PrivateQueryGotZoneData
 
 Revision 1.455  2007/09/05 20:53:06  cheshire
 Tidied up alignment of code layout; code was clearing m->tcpAddrInfo.sock instead of m->tcpDeviceInfo.sock
@@ -166,7 +277,7 @@ Fixed posix build error (mixed declarations and code)
 
 Revision 1.453  2007/09/05 02:26:57  cheshire
 <rdar://problem/5457287> mDNSResponder taking up 100% CPU in ReissueBlockedQuestions
-In startPrivateQueryCallback, restore q->ThisQInterval to non-zero value after GetZoneData completes
+In PrivateQueryGotZoneData, restore q->ThisQInterval to non-zero value after GetZoneData completes
 
 Revision 1.452  2007/08/31 22:58:22  cheshire
 If we have an existing TCP connection we should re-use it instead of just bailing out
@@ -1032,10 +1143,42 @@ mDNSlocal void unlinkSRS(mDNS *const m, ServiceRecordSet *srs)
 // (for service record sets, use RR_SRV as representative for time checks
 mDNSlocal void SetRecordRetry(mDNS *const m, AuthRecord *rr, mStatus SendErr)
        {
+       mDNSs32 elapsed = m->timenow - rr->LastAPTime;
        rr->LastAPTime = m->timenow;
-       if (SendErr == mStatus_TransientErr || rr->ThisAPInterval < INIT_UCAST_POLL_INTERVAL)  rr->ThisAPInterval = INIT_UCAST_POLL_INTERVAL;
-       else if (rr->ThisAPInterval*2 <= MAX_UCAST_POLL_INTERVAL)                              rr->ThisAPInterval *= 2;
-       else                                                                                   rr->ThisAPInterval = MAX_UCAST_POLL_INTERVAL;
+
+#if 0
+       // Code for stress-testing registration renewal code
+       if (rr->expire && rr->expire - m->timenow > mDNSPlatformOneSecond * 120)
+               {
+               LogOperation("Adjusting expiry from %d to 120 seconds for %s",
+                       (rr->expire - m->timenow) / mDNSPlatformOneSecond, ARDisplayString(m, rr));
+               rr->expire = m->timenow + mDNSPlatformOneSecond * 120;
+               }
+#endif
+
+       if (rr->expire && rr->expire - m->timenow > mDNSPlatformOneSecond)
+               {
+               mDNSs32 remaining = rr->expire - m->timenow;
+               rr->ThisAPInterval = remaining/2 + mDNSRandom(remaining/10);
+               LogOperation("SetRecordRetry refresh in %4d of %4d for %s",
+                       rr->ThisAPInterval / mDNSPlatformOneSecond,
+                       (rr->expire - m->timenow) / mDNSPlatformOneSecond,
+                       ARDisplayString(m, rr));
+               return;
+               }
+
+       rr->expire = 0;
+
+       // If at least half our our time interval has elapsed, it's time to double rr->ThisAPInterval
+       // If resulting interval is too small, set to at least INIT_UCAST_POLL_INTERVAL (3 seconds)
+       // If resulting interval is too large, set to at most 30 minutes
+       if (rr->ThisAPInterval / 2 <= elapsed) rr->ThisAPInterval *= 2;
+       if (rr->ThisAPInterval < INIT_UCAST_POLL_INTERVAL || SendErr == mStatus_TransientErr)
+               rr->ThisAPInterval = INIT_UCAST_POLL_INTERVAL;
+       if (rr->ThisAPInterval > 30 * 60 * mDNSPlatformOneSecond)
+               rr->ThisAPInterval = 30 * 60 * mDNSPlatformOneSecond;
+
+       LogOperation("SetRecordRetry retry   in %4d for %s", rr->ThisAPInterval / mDNSPlatformOneSecond, ARDisplayString(m, rr));
        }
 
 // ***************************************************************************
@@ -1194,16 +1337,13 @@ mDNSexport mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info,
        // Check to see if adding this new DomainAuthInfo has changed the credentials for any of our questions
        for (q = m->Questions; q; q=q->next)
                {
-               if (q->QuestionCallback != GetZoneData_QuestionCallback)
+               DomainAuthInfo *newinfo = GetAuthInfoForQuestion(m, q);
+               if (q->AuthInfo != newinfo)
                        {
-                       DomainAuthInfo *newinfo = GetAuthInfoForName_internal(m, &q->qname);
-                       if (q->AuthInfo != newinfo)
-                               {
-                               debugf("mDNS_SetSecretForDomain updating q->AuthInfo from %##s to %##s for %##s (%s)",
-                                       q->AuthInfo ? q->AuthInfo->domain.c : mDNSNULL,
-                                       newinfo     ? newinfo    ->domain.c : mDNSNULL, q->qname.c, DNSTypeName(q->qtype));
-                               q->AuthInfo = newinfo;
-                               }
+                       debugf("mDNS_SetSecretForDomain updating q->AuthInfo from %##s to %##s for %##s (%s)",
+                               q->AuthInfo ? q->AuthInfo->domain.c : mDNSNULL,
+                               newinfo     ? newinfo    ->domain.c : mDNSNULL, q->qname.c, DNSTypeName(q->qtype));
+                       q->AuthInfo = newinfo;
                        }
                }
 
@@ -1444,29 +1584,18 @@ mDNSexport mStatus mDNS_StopNATOperation(mDNS *m, NATTraversalInfo *traversal)
 #pragma mark - Long-Lived Queries
 #endif
 
+// Lock must be held -- otherwise m->timenow is undefined
 mDNSlocal void StartLLQPolling(mDNS *const m, DNSQuestion *q)
        {
        LogOperation("StartLLQPolling: %##s", q->qname.c);
        q->state = LLQ_Poll;
-       q->ThisQInterval = LLQ_POLL_INTERVAL;
-       q->LastQTime     = m->timenow - q->ThisQInterval;       // trigger immediate poll
+       q->ThisQInterval = INIT_UCAST_POLL_INTERVAL;
+       // We want to send our poll query ASAP, but the "+ 1" is because if we set the time to now,
+       // we risk causing spurious "SendQueries didn't send all its queries" log messages
+       q->LastQTime     = m->timenow - q->ThisQInterval + 1;
        SetNextQueryTime(m, q);
        }
 
-// Forward reference
-mDNSlocal void LLQNatMapComplete(mDNS *m, NATTraversalInfo *n);
-
-mDNSlocal void StartLLQNatMap(mDNS *m, DNSQuestion *q)
-       {
-       LogOperation("StartLLQNatMap: LLQ_NatMapWaitUDP");
-       mDNSPlatformMemZero(&q->NATInfoUDP, sizeof(NATTraversalInfo));
-       q->NATInfoUDP.IntPort = q->NATInfoUDP.RequestedPort = m->UnicastPort4;
-       q->NATInfoUDP.Protocol = NATOp_MapUDP;
-       q->NATInfoUDP.clientCallback = LLQNatMapComplete;
-       q->NATInfoUDP.clientContext = q;        // Must be set non-null so we know this NATTraversalInfo object is in use
-       mDNS_StartNATOperation_internal(m, &q->NATInfoUDP);
-       }
-
 mDNSlocal mDNSu8 *putLLQ(DNSMessage *const msg, mDNSu8 *ptr, const DNSQuestion *const question, const LLQOptData *const data, mDNSBool includeQuestion)
        {
        AuthRecord rr;
@@ -1497,17 +1626,20 @@ mDNSlocal mDNSu8 *putLLQ(DNSMessage *const msg, mDNSu8 *ptr, const DNSQuestion *
        return ptr;
        }
 
-mDNSlocal mStatus constructQueryMsg(DNSMessage *msg, mDNSu8 **endPtr, DNSQuestion *const question)
+// Normally we'd just request event packets be sent directly to m->LLQNAT.ExternalPort, except...
+// with LLQs over TLS/TCP we're doing a weird thing where instead of requesting packets be sent to ExternalAddress:ExternalPort
+// we're requesting that packets be sent to ExternalPort, but at the source address of our outgoing TCP connection.
+// Normally, after going through the NAT gateway, the source address of our outgoing TCP connection is the same as ExternalAddress,
+// so this is fine, except when the TCP connection ends up going over a VPN tunnel instead.
+// To work around this, if we find that the source address for our TCP connection is not a private address, we tell the Dot Mac
+// LLQ server to send events to us directly at port 5353 on that address, instead of at our mapped external NAT port.
+
+mDNSlocal mDNSu16 GetLLQEventPort(const mDNS *const m, const mDNSAddr *const dst)
        {
-       InitializeDNSMessage(&msg->h, question->TargetQID, uQueryFlags);
-
-       *endPtr = putQuestion(msg, msg->data, msg->data + AbsoluteMaxDNSMessageData, &question->qname, question->qtype, question->qclass);
-       if (!*endPtr)
-           {
-           LogMsg("ERROR: Unicast query out of space in packet");
-           return mStatus_UnknownErr;
-           }
-       return mStatus_NoError;
+       mDNSAddr src;
+       mDNSPlatformSourceAddrForDest(&src, dst);
+       //LogMsg("GetLLQEventPort: src %#a for dst %#a (%d)", &src, dst, mDNSv4AddrIsRFC1918(&src.ip.v4) ? mDNSVal16(m->LLQNAT.ExternalPort) : 0);
+       return(mDNSv4AddrIsRFC1918(&src.ip.v4) ? mDNSVal16(m->LLQNAT.ExternalPort) : mDNSVal16(MulticastDNSPort));
        }
 
 // Normally called with llq set.
@@ -1515,18 +1647,12 @@ mDNSlocal mStatus constructQueryMsg(DNSMessage *msg, mDNSu8 **endPtr, DNSQuestio
 mDNSlocal void sendChallengeResponse(mDNS *const m, DNSQuestion *const q, const LLQOptData *llq)
        {
        mDNSu8 *responsePtr = m->omsg.data;
-       mStatus err;
        LLQOptData llqBuf;
 
        if (q->ntries++ == kLLQ_MAX_TRIES)
                {
-               LogMsg("sendChallengeResponse: %d failed attempts for LLQ %##s Will re-try in %d minutes",
-                          kLLQ_MAX_TRIES, q->qname.c, kLLQ_DEF_RETRY / 60);
-               q->state         = LLQ_Retry;
-               q->LastQTime     = m->timenow;
-               q->ThisQInterval = (kLLQ_DEF_RETRY * mDNSPlatformOneSecond);
-               SetNextQueryTime(m, q);
-               // !!!KRS give a callback error in these cases?
+               LogMsg("sendChallengeResponse: %d failed attempts for LLQ %##s", kLLQ_MAX_TRIES, q->qname.c);
+               StartLLQPolling(m,q);
                return;
                }
 
@@ -1534,7 +1660,7 @@ mDNSlocal void sendChallengeResponse(mDNS *const m, DNSQuestion *const q, const
                {
                llqBuf.vers     = kLLQ_Vers;
                llqBuf.llqOp    = kLLQOp_Setup;
-               llqBuf.err      = LLQErr_NoError;
+               llqBuf.err      = LLQErr_NoError;       // Don't need to tell server UDP notification port when sending over UDP
                llqBuf.id       = q->id;
                llqBuf.llqlease = q->origLease;
                llq = &llqBuf;
@@ -1544,66 +1670,42 @@ mDNSlocal void sendChallengeResponse(mDNS *const m, DNSQuestion *const q, const
        q->ThisQInterval = q->tcp ? 0 : (kLLQ_INIT_RESEND * q->ntries * mDNSPlatformOneSecond);         // If using TCP, don't need to retransmit
        SetNextQueryTime(m, q);
 
-       if (constructQueryMsg(&m->omsg, &responsePtr, q)) goto error;
-       responsePtr = putLLQ(&m->omsg, responsePtr, q, llq, mDNSfalse);
-       if (!responsePtr) { LogMsg("ERROR: sendChallengeResponse - putLLQ"); goto error; }
-
-       //LogOperation("sendChallengeResponse %#a:%d %d %p %d", &q->servAddr, mDNSVal16(q->servPort), q->tcp, q->AuthInfo, responsePtr - (mDNSu8 *)&m->omsg);
-       err = mDNSSendDNSMessage(m, &m->omsg, responsePtr, mDNSInterface_Any, &q->servAddr, q->servPort, q->tcp ? q->tcp->sock : mDNSNULL, q->AuthInfo);
-       if (err) debugf("ERROR: sendChallengeResponse - mDNSSendDNSMessage returned %ld", err);
-       // on error, we procede as normal and retry after the appropriate interval
+       // To simulate loss of challenge response packet, uncomment line below
+       //if (q->ntries == 1) return;
 
-       return;
-
-       error:
-       q->state = LLQ_Error;
+       InitializeDNSMessage(&m->omsg.h, q->TargetQID, uQueryFlags);
+       responsePtr = putQuestion(&m->omsg, responsePtr, m->omsg.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass);
+       if (responsePtr) responsePtr = putLLQ(&m->omsg, responsePtr, q, llq, mDNSfalse);
+       if (responsePtr) mDNSSendDNSMessage(m, &m->omsg, responsePtr, mDNSInterface_Any, &q->servAddr, q->servPort, q->tcp ? q->tcp->sock : mDNSNULL, q->AuthInfo);
+       else StartLLQPolling(m,q);
        }
 
-mDNSlocal void recvSetupResponse(mDNS *const m, mDNSu8 rcode, DNSQuestion *const q, const rdataOPT *opt)
+mDNSlocal void SetLLQTimer(mDNS *const m, DNSQuestion *const q, const LLQOptData *const llq)
        {
-       mStatus err = mStatus_NoError;
+       mDNSs32 lease = (mDNSs32)llq->llqlease * mDNSPlatformOneSecond;
+       q->origLease     = llq->llqlease;
+       q->LastQTime     = m->timenow;
+       q->expire        = m->timenow + lease;
+       q->ThisQInterval = lease/2 + mDNSRandom(lease/10);
+       SetNextQueryTime(m, q);
+       }
 
+mDNSlocal void recvSetupResponse(mDNS *const m, mDNSu8 rcode, DNSQuestion *const q, const LLQOptData *const llq)
+       {
        if (rcode && rcode != kDNSFlag1_RC_NXDomain)
-               { LogMsg("ERROR: recvSetupResponse %##s - rcode && rcode != kDNSFlag1_RC_NXDomain", q->qname.c); goto fail; }
+               { LogMsg("ERROR: recvSetupResponse %##s - rcode && rcode != kDNSFlag1_RC_NXDomain", q->qname.c); return; }
 
-       if (opt->OptData.llq.llqOp != kLLQOp_Setup)
-               { LogMsg("ERROR: recvSetupResponse %##s - bad op %d", q->qname.c, opt->OptData.llq.llqOp); goto fail; }
+       if (llq->llqOp != kLLQOp_Setup)
+               { LogMsg("ERROR: recvSetupResponse %##s - bad op %d", q->qname.c, llq->llqOp); return; }
 
-       if (opt->OptData.llq.vers != kLLQ_Vers)
-               { LogMsg("ERROR: recvSetupResponse %##s - bad vers %d", q->qname.c, opt->OptData.llq.vers); goto fail; }
+       if (llq->vers != kLLQ_Vers)
+               { LogMsg("ERROR: recvSetupResponse %##s - bad vers %d", q->qname.c, llq->vers); return; }
 
        if (q->state == LLQ_InitialRequest)
                {
-               const LLQOptData *const llq = &opt->OptData.llq;
                //LogOperation("Got LLQ_InitialRequest");
 
-               switch(llq->err)
-                       {
-                       case LLQErr_NoError: break;
-                       case LLQErr_ServFull:
-                               LogMsg("recvSetupResponse - received ServFull from server for LLQ %##s Retry in %lu sec", q->qname.c, llq->llqlease);
-                               q->LastQTime     = m->timenow;
-                               q->ThisQInterval = ((mDNSs32)llq->llqlease * mDNSPlatformOneSecond);
-                               q->state = LLQ_Retry;
-                               SetNextQueryTime(m, q);
-                       case LLQErr_Static:
-                               q->state = LLQ_Static;
-                               q->ThisQInterval = 0;
-                               LogMsg("LLQ %##s: static", q->qname.c);
-                               goto exit;
-                       case LLQErr_FormErr:
-                               LogMsg("ERROR: recvSetupResponse - received FormErr from server for LLQ %##s", q->qname.c);
-                               goto error;
-                       case LLQErr_BadVers:
-                               LogMsg("ERROR: recvSetupResponse - received BadVers from server");
-                               goto error;
-                       case LLQErr_UnknownErr:
-                               LogMsg("ERROR: recvSetupResponse - received UnknownErr from server for LLQ %##s", q->qname.c);
-                               goto error;
-                       default:
-                               LogMsg("ERROR: recvSetupResponse - received invalid error %d for LLQ %##s", llq->err, q->qname.c);
-                               goto error;
-                       }
+               if (llq->err) { LogMsg("recvSetupResponse - received llq->err %d from server", llq->err); StartLLQPolling(m,q); return; }
        
                if (q->origLease != llq->llqlease)
                        debugf("recvSetupResponse: requested lease %lu, granted lease %lu", q->origLease, llq->llqlease);
@@ -1615,18 +1717,10 @@ mDNSlocal void recvSetupResponse(mDNS *const m, mDNSu8 rcode, DNSQuestion *const
                // update state
                q->state  = LLQ_SecondaryRequest;
                q->id     = llq->id;
-               // if (q->ntries == 1) goto exit;       // Test for simulating loss of challenge response packet
                q->ntries = 0; // first attempt to send response
-       
                sendChallengeResponse(m, q, llq);
-               goto exit;
-       
-               error:
-               q->state = LLQ_Error;
-               goto exit;
                }
-
-       if (q->state == LLQ_SecondaryRequest)
+       else if (q->state == LLQ_SecondaryRequest)
                {
                //LogOperation("Got LLQ_SecondaryRequest");
 
@@ -1636,119 +1730,92 @@ mDNSlocal void recvSetupResponse(mDNS *const m, mDNSu8 rcode, DNSQuestion *const
                // if the server sends back SERVFULL or STATIC.
                if (q->AuthInfo)
                        {
-                       LogOperation("Private LLQ_SecondaryRequest; copying id %08X%08X", opt->OptData.llq.id.l[0], opt->OptData.llq.id.l[1]);
-                       q->id = opt->OptData.llq.id;
+                       LogOperation("Private LLQ_SecondaryRequest; copying id %08X%08X", llq->id.l[0], llq->id.l[1]);
+                       q->id = llq->id;
                        }
 
-               if (opt->OptData.llq.err) { LogMsg("ERROR: recvSetupResponse %##s code %d from server", q->qname.c, opt->OptData.llq.err); q->state = LLQ_Error; goto fail; }
-               if (!mDNSSameOpaque64(&q->id, &opt->OptData.llq.id))
-                       { LogMsg("recvSetupResponse - ID changed.  discarding"); goto exit; } // this can happen rarely (on packet loss + reordering)
-               q->expire        = m->timenow + ((mDNSs32)opt->OptData.llq.llqlease *  mDNSPlatformOneSecond    );
-               q->LastQTime     = m->timenow;
-               q->ThisQInterval =              ((mDNSs32)opt->OptData.llq.llqlease * (mDNSPlatformOneSecond / 2));
-               q->origLease     = opt->OptData.llq.llqlease;
+               if (llq->err) { LogMsg("ERROR: recvSetupResponse %##s code %d from server", q->qname.c, llq->err); StartLLQPolling(m,q); return; }
+               if (!mDNSSameOpaque64(&q->id, &llq->id))
+                       { LogMsg("recvSetupResponse - ID changed.  discarding"); return; } // this can happen rarely (on packet loss + reordering)
                q->state         = LLQ_Established;
-               SetNextQueryTime(m, q);
-               goto exit;
-               }
-
-       if (q->state == LLQ_Established) goto exit;
-
-       LogMsg("ERROR: recvSetupResponse %##s - bad state %d", q->qname.c, q->state);
-
-fail:
-       err = mStatus_UnknownErr;
-
-exit:
-
-       if (err)
-               {
-               LogOperation("recvSetupResponse error %d ", err);
-               StartLLQPolling(m, q);
+               q->ntries        = 0;
+               SetLLQTimer(m, q, llq);
                }
        }
 
-// Returns mDNStrue if mDNSCoreReceiveResponse should treat this packet as a series of add/remove instructions (like an mDNS response)
-// Returns mDNSfalse if mDNSCoreReceiveResponse should treat this as a single authoritative result (like a normal unicast DNS response)
 mDNSexport uDNS_LLQType uDNS_recvLLQResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport)
        {
        DNSQuestion pktQ, *q;
        if (msg->h.numQuestions && getQuestion(msg, msg->data, end, 0, &pktQ))
                {
                const rdataOPT *opt = GetLLQOptData(m, msg, end);
-               if (opt)
+
+               for (q = m->Questions; q; q = q->next)
                        {
-                       for (q = m->Questions; q; q = q->next)
+                       if (!mDNSOpaque16IsZero(q->TargetQID) && q->LongLived && q->qtype == pktQ.qtype && q->qnamehash == pktQ.qnamehash && SameDomainName(&q->qname, &pktQ.qname))
                                {
-                               if (q->LongLived && q->qtype == pktQ.qtype && q->qnamehash == pktQ.qnamehash && SameDomainName(&q->qname, &pktQ.qname))
+                               debugf("uDNS_recvLLQResponse found %##s (%s) %d %#a %#a %X %X %X %X %d",
+                                       q->qname.c, DNSTypeName(q->qtype), q->state, srcaddr, &q->servAddr,
+                                       opt->OptData.llq.id.l[0], opt->OptData.llq.id.l[1], q->id.l[0], q->id.l[1], opt->OptData.llq.llqOp);
+                               if (q->state == LLQ_Poll)
                                        {
-                                       debugf("uDNS_recvLLQResponse found %##s (%s) %d %#a %#a %X %X %X %X %d",
-                                               q->qname.c, DNSTypeName(q->qtype), q->state, srcaddr, &q->servAddr,
-                                               opt->OptData.llq.id.l[0], opt->OptData.llq.id.l[1], q->id.l[0], q->id.l[1], opt->OptData.llq.llqOp);
-                                       if (q->state == LLQ_Poll)
-                                               {
-                                               m->rec.r.resrec.RecordType = 0;         // Clear RecordType to show we're not still using it
-                                               return uDNS_LLQ_Poll;
-                                               }
-                                       else if (q->state == LLQ_Established || (q->state == LLQ_Refresh && msg->h.numAnswers))
+                                       m->rec.r.resrec.RecordType = 0;         // Clear RecordType to show we're not still using it
+                                       LogOperation("uDNS_recvLLQResponse got poll response; moving to LLQ_InitialRequest for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+                                       q->state         = LLQ_InitialRequest;
+                                       q->servPort      = zeroIPPort;          // Clear servPort so that startLLQHandshake will retry the GetZoneData processing
+                                       q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10);        // Retry LLQ setup in approx 15 minutes
+                                       q->LastQTime     = m->timenow;
+                                       SetNextQueryTime(m, q);
+                                       return uDNS_LLQ_Entire;         // uDNS_LLQ_Entire means flush stale records; assume a large effective TTL
+                                       }
+                               else if (opt && q->state == LLQ_Established && opt->OptData.llq.llqOp == kLLQOp_Event && mDNSSameOpaque64(&opt->OptData.llq.id, &q->id))
+                                       {
+                                       mDNSu8 *ackEnd;
+                                       if (q->ThisQInterval < MAX_UCAST_POLL_INTERVAL) q->ThisQInterval = MAX_UCAST_POLL_INTERVAL;
+                                       //debugf("Sending LLQ ack for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+                                       InitializeDNSMessage(&m->omsg.h, msg->h.id, ResponseFlags);
+                                       ackEnd = putLLQ(&m->omsg, m->omsg.data, mDNSNULL, &opt->OptData.llq, mDNSfalse);
+                                       if (ackEnd) mDNSSendDNSMessage(m, &m->omsg, ackEnd, mDNSInterface_Any, srcaddr, srcport, mDNSNULL, mDNSNULL);
+                                       m->rec.r.resrec.RecordType = 0;         // Clear RecordType to show we're not still using it
+                                       return uDNS_LLQ_Events;
+                                       }
+                               if (opt && mDNSSameOpaque16(msg->h.id, q->TargetQID))
+                                       {
+                                       if (q->state == LLQ_Established && opt->OptData.llq.llqOp == kLLQOp_Refresh && mDNSSameOpaque64(&opt->OptData.llq.id, &q->id) && msg->h.numAdditionals && !msg->h.numAnswers)
                                                {
-                                               if (opt->OptData.llq.llqOp == kLLQOp_Event && mDNSSameOpaque64(&opt->OptData.llq.id, &q->id))
+                                               if (opt->OptData.llq.err != LLQErr_NoError) LogMsg("recvRefreshReply: received error %d from server", opt->OptData.llq.err);
+                                               else
                                                        {
-                                                       mDNSu8 *ackEnd;
-                                                       if (q->LongLived && q->state == LLQ_Poll && !mDNSIPPortIsZero(q->servPort)) q->ThisQInterval = LLQ_POLL_INTERVAL;
-                                                       else if (q->ThisQInterval < MAX_UCAST_POLL_INTERVAL)                        q->ThisQInterval = MAX_UCAST_POLL_INTERVAL;
-                                                       //debugf("Sending LLQ ack for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
-                                                       InitializeDNSMessage(&m->omsg.h, msg->h.id, ResponseFlags);
-                                                       ackEnd = putLLQ(&m->omsg, m->omsg.data, mDNSNULL, &opt->OptData.llq, mDNSfalse);
-                                                       if (ackEnd) mDNSSendDNSMessage(m, &m->omsg, ackEnd, mDNSInterface_Any, srcaddr, srcport, mDNSNULL, mDNSNULL);
-                                                       m->rec.r.resrec.RecordType = 0;         // Clear RecordType to show we're not still using it
-                                                       return uDNS_LLQ_Events;
+                                                       //LogOperation("Received refresh confirmation ntries %d for %##s (%s)", q->ntries, q->qname.c, DNSTypeName(q->qtype));
+                                                       GrantCacheExtensions(m, q, opt->OptData.llq.llqlease);
+                                                       SetLLQTimer(m, q, &opt->OptData.llq);
+                                                       q->ntries = 0;
                                                        }
+                                               m->rec.r.resrec.RecordType = 0;         // Clear RecordType to show we're not still using it
+                                               return uDNS_LLQ_Ignore;
                                                }
-                                       if (mDNSSameOpaque16(msg->h.id, q->TargetQID))
+                                       if (q->state < LLQ_Established && mDNSSameAddress(srcaddr, &q->servAddr))
                                                {
-                                               if (opt->OptData.llq.llqOp == kLLQOp_Refresh && q->state == LLQ_Refresh && msg->h.numAdditionals && !msg->h.numAnswers && mDNSSameOpaque64(&opt->OptData.llq.id, &q->id))
-                                                       {
-                                                       if (opt->OptData.llq.err != LLQErr_NoError) LogMsg("recvRefreshReply: received error %d from server", opt->OptData.llq.err);
-                                                       else
-                                                               {
-                                                               //LogOperation("Received refresh confirmation ntries %d for %##s (%s)", q->ntries, q->qname.c, DNSTypeName(q->qtype));
-                                                               GrantCacheExtensions(m, q, opt->OptData.llq.llqlease);
-                                                               q->expire   = q->LastQTime + ((mDNSs32)opt->OptData.llq.llqlease *  mDNSPlatformOneSecond   );
-                                                               q->ThisQInterval =           ((mDNSs32)opt->OptData.llq.llqlease * (mDNSPlatformOneSecond/2));
-                                                               q->origLease = opt->OptData.llq.llqlease;
-                                                               q->state = LLQ_Established;
-                                                               }
-                                                       m->rec.r.resrec.RecordType = 0;         // Clear RecordType to show we're not still using it
-                                                       return uDNS_LLQ_Events;
-                                                       }
-                                               if (q->state < LLQ_Static)
-                                                       {
-                                                       if (mDNSSameAddress(srcaddr, &q->servAddr))
-                                                               {
-                                                               LLQ_State oldstate = q->state;
-                                                               recvSetupResponse(m, msg->h.flags.b[1] & kDNSFlag1_RC_Mask, q, opt);
-                                                               m->rec.r.resrec.RecordType = 0;         // Clear RecordType to show we're not still using it
-                                                               //DumpPacket(m, msg, end);
-                                                               // If this is our Ack+Answers packet resulting from a correct challenge response, then it's a full list
-                                                               // of answers, and any cache answers we have that are not included in this packet need to be flushed
-                                                               //LogMsg("uDNS_recvLLQResponse: recvSetupResponse state %d returning %d", oldstate, oldstate != LLQ_SecondaryRequest);
-                                                               return (oldstate == LLQ_SecondaryRequest ? uDNS_LLQ_Setup : uDNS_LLQ_Events);
-                                                               }
-                                                       }
+                                               LLQ_State oldstate = q->state;
+                                               recvSetupResponse(m, msg->h.flags.b[1] & kDNSFlag1_RC_Mask, q, &opt->OptData.llq);
+                                               m->rec.r.resrec.RecordType = 0;         // Clear RecordType to show we're not still using it
+                                               // We have a protocol anomaly here in the LLQ definition.
+                                               // Both the challenge packet from the server and the ack+answers packet have opt->OptData.llq.llqOp == kLLQOp_Setup.
+                                               // However, we need to treat them differently:
+                                               // The challenge packet has no answers in it, and tells us nothing about whether our cache entries
+                                               // are still valid, so this packet should not cause us to do anything that messes with our cache.
+                                               // The ack+answers packet gives us the whole truth, so we should handle it by updating our cache
+                                               // to match the answers in the packet, and only the answers in the packet.
+                                               return (oldstate == LLQ_SecondaryRequest ? uDNS_LLQ_Entire : uDNS_LLQ_Ignore);
                                                }
                                        }
                                }
-                       m->rec.r.resrec.RecordType = 0;         // Clear RecordType to show we're not still using it
-                       //LogMsg("uDNS_recvLLQResponse: returning 0");
                        }
+               m->rec.r.resrec.RecordType = 0;         // Clear RecordType to show we're not still using it
                }
        return uDNS_LLQ_Not;
        }
 
-// Forward declaration
-mDNSlocal void sendLLQRefresh(mDNS *m, DNSQuestion *q, mDNSu32 lease);
-
 // Stub definition of TCPSocket_struct so we can access flags field. (Rest of TCPSocket_struct is platform-dependent.)
 struct TCPSocket_struct { TCPSocketFlags flags; /* ... */ };
 
@@ -1760,10 +1827,6 @@ mDNSlocal void tcpCallback(TCPSocket *sock, void *context, mDNSBool ConnectionEs
        mDNSBool   closed  = mDNSfalse;
        mDNSu8    *end;
        mDNS      *m       = tcpInfo->m;
-       DomainAuthInfo *AuthInfo =
-               tcpInfo->question ? tcpInfo->question->AuthInfo :
-               tcpInfo->srs      ? GetAuthInfoForName(m, tcpInfo->srs->RR_SRV.resrec.name)  :
-               tcpInfo->rr       ? GetAuthInfoForName(m, tcpInfo->rr->resrec.name) : mDNSNULL;
 
        tcpInfo_t **backpointer =
                tcpInfo->question ? &tcpInfo->question->tcp :
@@ -1778,8 +1841,9 @@ mDNSlocal void tcpCallback(TCPSocket *sock, void *context, mDNSBool ConnectionEs
 
        if (ConnectionEstablished)
                {
+               DomainAuthInfo *AuthInfo;
                // connection is established - send the message
-               if (tcpInfo->question && tcpInfo->question->LongLived && (tcpInfo->question->state == LLQ_Established || tcpInfo->question->state == LLQ_Refresh))
+               if (tcpInfo->question && tcpInfo->question->LongLived && tcpInfo->question->state == LLQ_Established)
                        {
                        //LogMsg("tcpCallback calling sendLLQRefresh %##s (%s)", tcpInfo->question->qname.c, DNSTypeName(tcpInfo->question->qtype));
                        mDNS_Lock(m);
@@ -1787,31 +1851,47 @@ mDNSlocal void tcpCallback(TCPSocket *sock, void *context, mDNSBool ConnectionEs
                        mDNS_Unlock(m);
                        return;
                        }
-               else if (tcpInfo->question && tcpInfo->question->LongLived && tcpInfo->question->state != LLQ_Poll)
+               else if (tcpInfo->question && tcpInfo->question->LongLived && tcpInfo->question->state != LLQ_Poll && !mDNSIPPortIsZero(m->LLQNAT.ExternalPort))
                        {
+                       // Notes:
+                       // If we have a NAT port mapping, ExternalPort is the external port
+                       // If we have a routable address so we don't need a port mapping, ExternalPort is the same as our own internal port
+                       // If we need a NAT port mapping but can't get one, then ExternalPort is zero
                        LLQOptData llqData;                     // set llq rdata
                        llqData.vers  = kLLQ_Vers;
                        llqData.llqOp = kLLQOp_Setup;
-                       llqData.err   = LLQErr_NoError;
-                       llqData.err   = mDNSVal16(tcpInfo->question->eventPort);                // Tell server our external NAT-mapped UDP port
-                       if (llqData.err == 0) llqData.err = 5353;       // If not using NAT, then we need events sent directly to 5353
+                       llqData.err   = GetLLQEventPort(m, &tcpInfo->Addr);     // We're using TCP; tell server what UDP port to send notifications to
                        LogOperation("tcpCallback: eventPort %d", llqData.err);
                        llqData.id    = zeroOpaque64;
                        llqData.llqlease = kLLQ_DefLease;
                        InitializeDNSMessage(&tcpInfo->request.h, tcpInfo->question->TargetQID, uQueryFlags);
-                       //LogMsg("tcpCallback: putLLQ %p", AuthInfo);
                        end = putLLQ(&tcpInfo->request, tcpInfo->request.data, tcpInfo->question, &llqData, mDNStrue);
-
                        if (!end) { LogMsg("ERROR: tcpCallback - putLLQ"); err = mStatus_UnknownErr; goto exit; }
                        }
                else if (tcpInfo->question)
                        {
-                       err = constructQueryMsg(&tcpInfo->request, &end, tcpInfo->question);
-                       if (err) { LogMsg("ERROR: tcpCallback: constructQueryMsg - %ld", err); err = mStatus_UnknownErr; goto exit; }
+                       DNSQuestion *q = tcpInfo->question;
+                       InitializeDNSMessage(&tcpInfo->request.h, q->TargetQID, uQueryFlags);
+                       end = putQuestion(&tcpInfo->request, tcpInfo->request.data, tcpInfo->request.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass);
                        }
                else
                        end = ((mDNSu8*) &tcpInfo->request) + tcpInfo->requestLen;
 
+               // Defensive coding for <rdar://problem/5546824> Crash in mDNSResponder at GetAuthInfoForName_internal + 366
+               // Don't know yet what's causing this, but at least we can be cautious and try to avoid crashing if we find our pointers in an unexpected state
+               if (tcpInfo->srs && tcpInfo->srs->RR_SRV.resrec.name != &tcpInfo->srs->RR_SRV.namestorage)
+                       LogMsg("tcpCallback: ERROR: tcpInfo->srs->RR_SRV.resrec.name %p != &tcpInfo->srs->RR_SRV.namestorage %p",
+                               tcpInfo->srs->RR_SRV.resrec.name, &tcpInfo->srs->RR_SRV.namestorage);
+               if (tcpInfo->rr && tcpInfo->rr->resrec.name != &tcpInfo->rr->namestorage)
+                       LogMsg("tcpCallback: ERROR: tcpInfo->rr->resrec.name %p != &tcpInfo->rr->namestorage %p",
+                               tcpInfo->rr->resrec.name, &tcpInfo->rr->namestorage);
+               if (tcpInfo->srs && tcpInfo->srs->RR_SRV.resrec.name != &tcpInfo->srs->RR_SRV.namestorage) return;
+               if (tcpInfo->rr  && tcpInfo->rr->        resrec.name != &tcpInfo->rr->        namestorage) return;
+
+               AuthInfo =      tcpInfo->question ? tcpInfo->question->AuthInfo :
+                                       tcpInfo->srs      ? GetAuthInfoForName(m, tcpInfo->srs->RR_SRV.resrec.name) :
+                                       tcpInfo->rr       ? GetAuthInfoForName(m, tcpInfo->rr->resrec.name) : mDNSNULL;
+
                err = mDNSSendDNSMessage(m, &tcpInfo->request, end, mDNSInterface_Any, &tcpInfo->Addr, tcpInfo->Port, sock, AuthInfo);
 
                if (err) { debugf("ERROR: tcpCallback: mDNSSendDNSMessage - %ld", err); err = mStatus_UnknownErr; goto exit; }
@@ -1902,58 +1982,27 @@ exit:
                // we won't end up double-disposing our tcpInfo_t
                *backpointer = mDNSNULL;
 
+               mDNS_Lock(m);           // Need to grab the lock to get m->timenow
+
                if (tcpInfo->question)
                        {
                        DNSQuestion *q = tcpInfo->question;
-                       mDNS_Lock(m);           // Need to grab the lock to get m->timenow
                        if (q->ThisQInterval == 0 || q->LastQTime + q->ThisQInterval - m->timenow > MAX_UCAST_POLL_INTERVAL)
                                {
                                q->LastQTime     = m->timenow;
                                q->ThisQInterval = MAX_UCAST_POLL_INTERVAL;
                                SetNextQueryTime(m, q);
                                }
-                       mDNS_Unlock(m);
                        // ConnFailed is actually okay.  It just means that the server closed the connection but the LLQ is still okay.
                        // If the error isn't ConnFailed, then the LLQ is in bad shape.
-                       if (err != mStatus_ConnFailed) tcpInfo->question->state = LLQ_Error;
-                       }
-
-               if (tcpInfo->rr)
-                       {
-                       mDNSBool deregPending = (tcpInfo->rr->state == regState_DeregPending) ? mDNStrue : mDNSfalse;
-
-                       UnlinkAuthRecord(m, tcpInfo->rr);
-                       tcpInfo->rr->state = regState_Unregistered;
-
-                       if (!deregPending)
-                               {
-                               // Right now tcpCallback does not run holding the lock, so no need to drop the lock
-                               //mDNS_DropLockBeforeCallback();
-                               if (tcpInfo->rr->RecordCallback)
-                                       tcpInfo->rr->RecordCallback(m, tcpInfo->rr, err);
-                               //mDNS_ReclaimLockAfterCallback();
-                               // NOTE: not safe to touch any client structures here --
-                               // once we issue the callback, client is free to reuse or deallocate the srs memory
-                               }
+                       if (err != mStatus_ConnFailed) StartLLQPolling(m, tcpInfo->question);
                        }
 
-               if (tcpInfo->srs)
-                       {
-                       mDNSBool deregPending = (tcpInfo->srs->state == regState_DeregPending) ? mDNStrue : mDNSfalse;
+               if (tcpInfo->rr) SetRecordRetry(m, tcpInfo->rr, mStatus_NoError);
 
-                       unlinkSRS(m, tcpInfo->srs);
-                       tcpInfo->srs->state = regState_Unregistered;
+               if (tcpInfo->srs) SetRecordRetry(m, &tcpInfo->srs->RR_SRV, mStatus_NoError);
 
-                       if (!deregPending)
-                               {
-                               // Right now tcpCallback does not run holding the lock, so no need to drop the lock
-                               //mDNS_DropLockBeforeCallback();
-                               tcpInfo->srs->ServiceCallback(m, tcpInfo->srs, err);
-                               //mDNS_ReclaimLockAfterCallback();
-                               // NOTE: not safe to touch any client structures here --
-                               // once we issue the callback, client is free to reuse or deallocate the srs memory
-                               }
-                       }
+               mDNS_Unlock(m);
 
                DisposeTCPConn(tcpInfo);
                }
@@ -1985,8 +2034,15 @@ mDNSlocal tcpInfo_t *MakeTCPConn(mDNS *const m, const DNSMessage *const msg, con
        if (!info->sock) { LogMsg("SendServiceRegistration: uanble to create TCP socket"); mDNSPlatformMemFree(info); return(mDNSNULL); }
        err = mDNSPlatformTCPConnect(info->sock, Addr, Port, 0, tcpCallback, info);
 
+       // Probably suboptimal here.
+       // Instead of returning mDNSNULL here on failure, we should probably invoke the callback with an error code.
+       // That way clients can put all the error handling and retry/recovery code in one place,
+       // instead of having to handle immediate errors in one place and async errors in another.
+       // Also: "err == mStatus_ConnEstablished" probably never happens.
+
+       // Don't need to log "connection failed" in customer builds -- it happens quite often during sleep, wake, configuration changes, etc.
        if      (err == mStatus_ConnEstablished) { tcpCallback(info->sock, info, mDNStrue, mStatus_NoError); }
-       else if (err != mStatus_ConnPending    ) { LogMsg("MakeTCPConnection: connection failed"); mDNSPlatformMemFree(info); return(mDNSNULL); }
+       else if (err != mStatus_ConnPending    ) { LogOperation("MakeTCPConnection: connection failed"); mDNSPlatformMemFree(info); return(mDNSNULL); }
        return(info);
        }
 
@@ -1997,142 +2053,94 @@ mDNSexport void DisposeTCPConn(struct tcpInfo_t *tcp)
        mDNSPlatformMemFree(tcp);
        }
 
-mDNSlocal void RemoveLLQNatMappings(mDNS *m, DNSQuestion *q)
+// Lock must be held
+mDNSexport void startLLQHandshake(mDNS *m, DNSQuestion *q)
        {
-       if (q->NATInfoUDP.clientContext)
+       if (mDNSIPv4AddressIsOnes(m->LLQNAT.ExternalAddress))
                {
-               mDNS_StopNATOperation_internal(m, &q->NATInfoUDP);
-               q->NATInfoUDP.clientContext = mDNSNULL;
+               LogOperation("startLLQHandshake: waiting for NAT status for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+               q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10);        // Retry in approx 15 minutes
+               q->LastQTime = m->timenow;
+               SetNextQueryTime(m, q);
+               return;
                }
-       }
 
-mDNSlocal void startLLQHandshake(mDNS *m, DNSQuestion *q)
-       {
-       mStatus err = mStatus_NoError;
-       if (q->AuthInfo)
+       if (mDNSIPPortIsZero(m->LLQNAT.ExternalPort))
                {
-               LogOperation("startLLQHandshakePrivate Addr %#a%s Server %#a:%d%s %##s (%s) eventport %d",
-                       &m->AdvertisedV4,                     mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) ? " (RFC 1918)" : "",
-                       &q->servAddr, mDNSVal16(q->servPort), mDNSAddrIsRFC1918(&q->servAddr)             ? " (RFC 1918)" : "",
-                       q->qname.c, DNSTypeName(q->qtype), mDNSVal16(q->eventPort));
-
-               if (mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) && !mDNSAddrIsRFC1918(&q->servAddr))
-                       if (q->state == LLQ_InitialRequest)
-                               {
-                               // start
-                               if (!q->NATInfoUDP.clientContext) { q->state = LLQ_NatMapWaitUDP; StartLLQNatMap(m, q); goto exit; }
-                               else
-                                       {
-                                       LogMsg("startLLQHandshake state == LLQ_InitialRequest but already have NATInfoUDP.clientContext %##s (%s)",
-                                               q->qname.c, DNSTypeName(q->qtype));
-                                       err = mStatus_UnknownErr;
-                                       goto exit;
-                                       }
-                               }
-
-               LogOperation("startLLQHandshake TCP %p %##s (%s)", q->tcp, q->qname.c, DNSTypeName(q->qtype));
-               if (q->tcp) LogMsg("startLLQHandshake: Already have TCP connection for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
-               else q->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_UseTLS, &q->servAddr, q->servPort, q, mDNSNULL, mDNSNULL);
+               LogOperation("startLLQHandshake: Cannot receive inbound packets; will poll for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+               StartLLQPolling(m, q);
+               return;
+               }
 
-               // update question state
-               //q->state         = LLQ_InitialRequest;
-               q->state         = LLQ_SecondaryRequest;                // Right now, for private DNS, we skip the four-way LLQ handshake
-               q->origLease     = kLLQ_DefLease;
-               q->ThisQInterval = 0;
+       if (mDNSIPPortIsZero(q->servPort))
+               {
+               LogOperation("startLLQHandshake: StartGetZoneData for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+               q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10);        // Retry in approx 15 minutes
                q->LastQTime     = m->timenow;
                SetNextQueryTime(m, q);
+               q->servAddr = zeroAddr;
+               q->servPort = zeroIPPort;
+               if (q->nta) CancelGetZoneData(m, q->nta);
+               q->nta = StartGetZoneData(m, &q->qname, ZoneServiceLLQ, LLQGotZoneData, q);
+               return;
+               }
 
-               err = mStatus_NoError;
+       if (q->AuthInfo)
+               {
+               if (q->tcp) LogOperation("startLLQHandshake: Disposing existing TCP connection for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+               if (q->tcp) DisposeTCPConn(q->tcp);
+               q->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_UseTLS, &q->servAddr, q->servPort, q, mDNSNULL, mDNSNULL);
+               if (!q->tcp)
+                       q->ThisQInterval = mDNSPlatformOneSecond * 5;   // If TCP failed (transient networking glitch) try again in five seconds
+               else
+                       {
+                       q->state         = LLQ_SecondaryRequest;                // Right now, for private DNS, we skip the four-way LLQ handshake
+                       q->origLease     = kLLQ_DefLease;
+                       q->ThisQInterval = 0;
+                       }
+               q->LastQTime     = m->timenow;
+               SetNextQueryTime(m, q);
                }
        else
                {
-               mDNSu8 *end;
-               LLQOptData llqData;
-
-               LogOperation("startLLQHandshake Addr %#a%s Server %#a:%d%s %##s (%s) RequestedPort %d",
+               LogOperation("startLLQHandshake m->AdvertisedV4 %#a%s Server %#a:%d%s %##s (%s)",
                        &m->AdvertisedV4,                     mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) ? " (RFC 1918)" : "",
                        &q->servAddr, mDNSVal16(q->servPort), mDNSAddrIsRFC1918(&q->servAddr)             ? " (RFC 1918)" : "",
-                       q->qname.c, DNSTypeName(q->qtype), mDNSVal16(q->NATInfoUDP.RequestedPort));
+                       q->qname.c, DNSTypeName(q->qtype));
 
-               if (mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) && !mDNSAddrIsRFC1918(&q->servAddr))
+               if (q->ntries++ >= kLLQ_MAX_TRIES)
                        {
-                       // start
-                       if (!q->NATInfoUDP.clientContext) { q->state = LLQ_NatMapWaitUDP; StartLLQNatMap(m, q); }
-                       else { err = mStatus_UnknownErr; goto exit; }
+                       LogMsg("startLLQHandshake: %d failed attempts for LLQ %##s Polling.", kLLQ_MAX_TRIES, q->qname.c);
+                       StartLLQPolling(m, q);
                        }
-
-               if (q->ntries++ >= kLLQ_MAX_TRIES)
-                       { LogMsg("startLLQHandshake: %d failed attempts for LLQ %##s Polling.", kLLQ_MAX_TRIES, q->qname.c); err = mStatus_UnknownErr; goto exit; }
-
-               // set llq rdata
-               llqData.vers  = kLLQ_Vers;
-               llqData.llqOp = kLLQOp_Setup;
-               llqData.err   = LLQErr_NoError;
-               llqData.id    = zeroOpaque64;
-               llqData.llqlease = kLLQ_DefLease;
-
-               InitializeDNSMessage(&m->omsg.h, q->TargetQID, uQueryFlags);
-               end = putLLQ(&m->omsg, m->omsg.data, q, &llqData, mDNStrue);
-               if (!end) { LogMsg("ERROR: startLLQHandshake - putLLQ"); q->state = LLQ_Error; return; }
-
-               err = mDNSSendDNSMessage(m, &m->omsg, end, mDNSInterface_Any, &q->servAddr, q->servPort, mDNSNULL, mDNSNULL);
-               // on error, we procede as normal and retry after the appropriate interval
-               if (err) { debugf("ERROR: startLLQHandshake - mDNSSendDNSMessage returned %ld", err); err = mStatus_NoError; }
-
-               // update question state
-               q->state         = LLQ_InitialRequest;
-               q->origLease     = kLLQ_DefLease;
-               q->ThisQInterval = (kLLQ_INIT_RESEND * mDNSPlatformOneSecond);
-               q->LastQTime     = m->timenow - q->ThisQInterval;
-               SetNextQueryTime(m, q);
-
-               err = mStatus_NoError;
-               }
-
-exit:
-       if (err)
-               {
-               LogOperation("startLLQHandshake error %d ", err);
-               StartLLQPolling(m, q);
-               }
-       }
-
-// Called in normal client context (lock not held)
-mDNSlocal void LLQNatMapComplete(mDNS *m, NATTraversalInfo *n)
-       {
-       if (m->CurrentQuestion)
-               LogMsg("LLQNatMapComplete: ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
-       m->CurrentQuestion = m->Questions;
-
-       while (m->CurrentQuestion)
-               {
-               DNSQuestion *q = m->CurrentQuestion;
-               m->CurrentQuestion = m->CurrentQuestion->next;
-               if (q->LongLived)
+               else
                        {
-                       if (q->state == LLQ_NatMapWaitUDP)
-                               {
-                               if (!n->Result)
-                                       {
-                                       q->state = LLQ_GetZoneInfo;
-                                       q->eventPort = n->ExternalPort;
-                                       startLLQHandshake(m, q);
-                                       }
-                               else
-                                       {
-                                       LogMsg("LLQNatMapComplete error %d Internal %d Requested %d External %d", n->Result,
-                                               mDNSVal16(q->NATInfoUDP.IntPort), mDNSVal16(q->NATInfoUDP.RequestedPort), mDNSVal16(q->NATInfoUDP.ExternalPort));
-                                       RemoveLLQNatMappings(m, q);
-                                       StartLLQPolling(m, q);
-                                       }
-                               }
+                       mDNSu8 *end;
+                       LLQOptData llqData;
+
+                       // set llq rdata
+                       llqData.vers  = kLLQ_Vers;
+                       llqData.llqOp = kLLQOp_Setup;
+                       llqData.err   = LLQErr_NoError; // Don't need to tell server UDP notification port when sending over UDP
+                       llqData.id    = zeroOpaque64;
+                       llqData.llqlease = kLLQ_DefLease;
+       
+                       InitializeDNSMessage(&m->omsg.h, q->TargetQID, uQueryFlags);
+                       end = putLLQ(&m->omsg, m->omsg.data, q, &llqData, mDNStrue);
+                       if (!end) { LogMsg("ERROR: startLLQHandshake - putLLQ"); StartLLQPolling(m,q); return; }
+       
+                       mDNSSendDNSMessage(m, &m->omsg, end, mDNSInterface_Any, &q->servAddr, q->servPort, mDNSNULL, mDNSNULL);
+       
+                       // update question state
+                       q->state         = LLQ_InitialRequest;
+                       q->origLease     = kLLQ_DefLease;
+                       q->ThisQInterval = (kLLQ_INIT_RESEND * mDNSPlatformOneSecond);
+                       q->LastQTime     = m->timenow;
+                       SetNextQueryTime(m, q);
                        }
                }
-       m->CurrentQuestion = mDNSNULL;
        }
 
-// if we ever want to refine support for multiple hostnames, we can add logic matching service names to a particular hostname
-// for now, we grab the first registered DynDNS name, if any, or a static name we learned via a reverse-map query
 mDNSexport const domainname *GetServiceTarget(mDNS *m, ServiceRecordSet *srs)
        {
        LogOperation("GetServiceTarget %##s", srs->RR_SRV.resrec.name->c);
@@ -2141,26 +2149,34 @@ mDNSexport const domainname *GetServiceTarget(mDNS *m, ServiceRecordSet *srs)
                return(&srs->RR_SRV.resrec.rdata->u.srv.target);
        else
                {
-               HostnameInfo *hi = m->Hostnames;
-
 #if APPLE_OSX_mDNSResponder
                DomainAuthInfo *AuthInfo = GetAuthInfoForName_internal(m, srs->RR_SRV.resrec.name);
                if (AuthInfo && AuthInfo->AutoTunnel)
                        {
-                       if (AuthInfo->AutoTunnelHostRecord.namestorage.c[0] == 0)
-                               {
-                               if (m->AutoTunnelHostAddr.b[0]) SetupLocalAutoTunnelInterface_internal(m);
-                               return(mDNSNULL);
-                               }
+                       // If this AutoTunnel is not yet active, start it now (which entails activating its NAT Traversal request,
+                       // which will subsequently advertise the appropriate records when the NAT Traversal returns a result)
+                       if (!AuthInfo->AutoTunnelNAT.clientContext && m->AutoTunnelHostAddr.b[0])
+                               SetupLocalAutoTunnelInterface_internal(m);
+                       if (AuthInfo->AutoTunnelHostRecord.namestorage.c[0] == 0) return(mDNSNULL);
                        return(&AuthInfo->AutoTunnelHostRecord.namestorage);
                        }
+               else
 #endif APPLE_OSX_mDNSResponder
-
-               while (hi)
                        {
-                       if (hi->arv4.state == regState_Registered || hi->arv4.state == regState_Refresh) return(hi->arv4.resrec.name);
-                       if (hi->arv6.state == regState_Registered || hi->arv6.state == regState_Refresh) return(hi->arv6.resrec.name);
-                       hi = hi->next;
+                       const int srvcount = CountLabels(srs->RR_SRV.resrec.name);
+                       HostnameInfo *besthi = mDNSNULL, *hi;
+                       int best = 0;
+                       for (hi = m->Hostnames; hi; hi = hi->next)
+                               if (hi->arv4.state == regState_Registered || hi->arv4.state == regState_Refresh ||
+                                       hi->arv6.state == regState_Registered || hi->arv6.state == regState_Refresh)
+                                       {
+                                       int x, hostcount = CountLabels(&hi->fqdn);
+                                       for (x = hostcount < srvcount ? hostcount : srvcount; x > 0 && x > best; x--)
+                                               if (SameDomainName(SkipLeadingLabels(srs->RR_SRV.resrec.name, srvcount - x), SkipLeadingLabels(&hi->fqdn, hostcount - x)))
+                                                       { best = x; besthi = hi; }
+                                       }
+       
+                       if (besthi) return(&besthi->fqdn);
                        }
                if (m->StaticHostname.c[0]) return(&m->StaticHostname);
                return(mDNSNULL);
@@ -2180,7 +2196,15 @@ mDNSlocal void SendServiceRegistration(mDNS *m, ServiceRecordSet *srs)
        if (m->mDNS_busy != m->mDNS_reentrancy+1)
                LogMsg("SendServiceRegistration: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
 
-       if (mDNSIPv4AddressIsZero(srs->ns.ip.v4)) { LogMsg("SendServiceRegistration - NS not set!"); return; }
+       if (mDNSIPv4AddressIsZero(srs->SRSUpdateServer.ip.v4))  // Don't know our UpdateServer yet
+               {
+               srs->RR_SRV.LastAPTime = m->timenow;
+               if (srs->RR_SRV.ThisAPInterval < mDNSPlatformOneSecond * 5)
+                       srs->RR_SRV.ThisAPInterval = mDNSPlatformOneSecond * 5;
+               return;
+               }
+
+       if (srs->state == regState_Registered) srs->state = regState_Refresh;
 
        id = mDNS_NewMessageID(m);
        InitializeDNSMessage(&m->omsg.h, id, UpdateReqFlags);
@@ -2237,9 +2261,9 @@ mDNSlocal void SendServiceRegistration(mDNS *m, ServiceRecordSet *srs)
                if (!(ptr = PutResourceRecordTTLJumbo(&m->omsg, ptr, &m->omsg.h.mDNS_numUpdates, &srs->RR_TXT.resrec, srs->RR_TXT.resrec.rroriginalttl))) { err = mStatus_UnknownErr; goto exit; }
 
        target = GetServiceTarget(m, srs);
-       if (!target)
+       if (!target || target->c[0] == 0)
                {
-               debugf("SendServiceRegistration - no target for %##s", srs->RR_SRV.resrec.name->c);
+               LogOperation("SendServiceRegistration - no target for %##s", srs->RR_SRV.resrec.name->c);
                srs->state = regState_NoTarget;
                return;
                }
@@ -2263,26 +2287,26 @@ mDNSlocal void SendServiceRegistration(mDNS *m, ServiceRecordSet *srs)
 
        if (srs->Private)
                {
-               LogOperation("SendServiceRegistration TCP %p %s", srs->tcp, ARDisplayString(m, &srs->RR_SRV));
-               if (srs->tcp) LogMsg("SendServiceRegistration: Already have TCP connection for %s", ARDisplayString(m, &srs->RR_SRV));
-               else srs->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &srs->ns, srs->SRSUpdatePort, mDNSNULL, srs, mDNSNULL);
-               srs->RR_SRV.LastAPTime = m->timenow;
-               srs->RR_SRV.ThisAPInterval = 0x3FFFFFFF; // TCP will handle any necessary retransmissions for us
+               if (srs->tcp) LogOperation("SendServiceRegistration: Disposing existing TCP connection for %s", ARDisplayString(m, &srs->RR_SRV));
+               if (srs->tcp) DisposeTCPConn(srs->tcp);
+               srs->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &srs->SRSUpdateServer, srs->SRSUpdatePort, mDNSNULL, srs, mDNSNULL);
+               if (!srs->tcp) srs->RR_SRV.ThisAPInterval = mDNSPlatformOneSecond * 5; // If failed to make TCP connection, try again in ten seconds (5*2)
+               else if (srs->RR_SRV.ThisAPInterval < mDNSPlatformOneSecond * 30) srs->RR_SRV.ThisAPInterval = mDNSPlatformOneSecond * 30;
                }
        else
                {
-               err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, &srs->ns, srs->SRSUpdatePort, mDNSNULL, GetAuthInfoForName_internal(m, srs->RR_SRV.resrec.name));
+               err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, &srs->SRSUpdateServer, srs->SRSUpdatePort, mDNSNULL, GetAuthInfoForName_internal(m, srs->RR_SRV.resrec.name));
                if (err) debugf("ERROR: SendServiceRegistration - mDNSSendDNSMessage - %ld", err);
-               SetRecordRetry(m, &srs->RR_SRV, err);
                }
 
-       err = mStatus_NoError;
+       SetRecordRetry(m, &srs->RR_SRV, err);
+       return;
 
 exit:
 
        if (err)
                {
-               LogMsg("SendServiceRegistration - Error formatting message %d", err);
+               LogMsg("SendServiceRegistration ERROR formatting message %d!! Permanently abandoning service registration %##s", err, srs->RR_SRV.resrec.name->c);
 
                unlinkSRS(m, srs);
                srs->state = regState_Unregistered;
@@ -2312,7 +2336,7 @@ mDNSlocal const domainname *PRIVATE_LLQ_SERVICE_TYPE    = (const domainname*)"\x
 mDNSlocal mStatus GetZoneData_StartQuery(mDNS *const m, ZoneData *zd, mDNSu16 qtype);
 
 // GetZoneData_QuestionCallback is called from normal client callback context (core API calls allowed)
-mDNSexport void GetZoneData_QuestionCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+mDNSlocal void GetZoneData_QuestionCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
        {
        ZoneData *zd = (ZoneData*)question->QuestionContext;
 
@@ -2341,7 +2365,7 @@ mDNSexport void GetZoneData_QuestionCallback(mDNS *const m, DNSQuestion *questio
                        }
                else
                        {
-                       LogMsg("ERROR: GetZoneData_QuestionCallback - recursed to root label of %##s without finding SOA", zd->ChildName.c);
+                       LogOperation("GetZoneData recursed to root label of %##s without finding SOA", zd->ChildName.c);
                        zd->ZoneDataCallback(m, mStatus_NoSuchNameErr, zd);
                        mDNSPlatformMemFree(zd);
                        }
@@ -2391,9 +2415,9 @@ mDNSlocal mStatus GetZoneData_StartQuery(mDNS *const m, ZoneData *zd, mDNSu16 qt
        {
        if (qtype == kDNSType_SRV)
                {
-               LogOperation("lookupDNSPort %##s", ZoneDataSRV(zd));
                AssignDomainName(&zd->question.qname, ZoneDataSRV(zd));
                AppendDomainName(&zd->question.qname, &zd->ZoneName);
+               LogOperation("lookupDNSPort %##s", zd->question.qname.c);
                }
 
        zd->question.ThisQInterval       = -1;          // So that GetZoneData_QuestionCallback() knows whether to cancel this question (Is this necessary?)
@@ -2443,22 +2467,16 @@ mDNSexport ZoneData *StartGetZoneData(mDNS *const m, const domainname *const nam
        return zd;
        }
 
-// if LLQ NAT context unreferenced, delete the mapping
-mDNSlocal void CheckForUnreferencedLLQMapping(mDNS *m)
+// GetZoneData queries are a special case -- even if we have a key for them, we don't do them privately,
+// because that would result in an infinite loop (i.e. to do a private query we first need to get
+// the _dns-query-tls SRV record for the zone, and we can't do *that* privately because to do so
+// we'd need to already know the _dns-query-tls SRV record.
+// Also, as a general rule, we never do SOA queries privately
+mDNSexport DomainAuthInfo *GetAuthInfoForQuestion(mDNS *m, const DNSQuestion *const q) // Must be called with lock held
        {
-       NATTraversalInfo *n = m->NATTraversals;
-       DNSQuestion *q;
-
-       while (n)
-               {
-               NATTraversalInfo *current = n;
-               n = n->next;
-               if (current->clientCallback == LLQNatMapComplete)
-                       {
-                       for (q = m->Questions; q; q = q->next) if (&q->NATInfoUDP == current) break;
-                       if (!q) mDNS_StopNATOperation_internal(m, current);
-                       }
-               }
+       if (q->QuestionCallback == GetZoneData_QuestionCallback) return(mDNSNULL);
+       if (q->qtype            == kDNSType_SOA                ) return(mDNSNULL);
+       return(GetAuthInfoForName_internal(m, &q->qname));
        }
 
 // ***************************************************************************
@@ -2475,35 +2493,16 @@ mDNSlocal void CompleteSRVNatMap(mDNS *m, NATTraversalInfo *n)
        if (!srs) { LogMsg("CompleteSRVNatMap called with unknown ServiceRecordSet object"); return; }
        if (!n->NATLease) return;
 
-       if (n->Result)
-               {
-               HostnameInfo *hi = m->Hostnames;
-               while (hi)
-                       {
-                       if (hi->arv6.state == regState_Registered || hi->arv6.state == regState_Refresh) break;
-                       else hi = hi->next;
-                       }
-
-               if (hi)
-                       {
-                       debugf("Port map failed for service %##s - using IPv6 service target", srs->RR_SRV.resrec.name->c);
-                       mDNS_StopNATOperation(m, &srs->NATinfo);
-                       goto register_service;
-                       }
-               else srs->state = regState_NATError;
-               }
-
-       register_service:
        mDNS_Lock(m);
-       if (!mDNSIPv4AddressIsZero(srs->ns.ip.v4))
+       if (!mDNSIPv4AddressIsZero(srs->SRSUpdateServer.ip.v4))
                SendServiceRegistration(m, srs);        // non-zero server address means we already have necessary zone data to send update
        else
                {
                // SHOULD NEVER HAPPEN!
-               LogOperation("ERROR: CompleteSRVNatMap called but srs->ns.ip.v4 is zero!");
+               LogOperation("ERROR: CompleteSRVNatMap called but srs->SRSUpdateServer.ip.v4 is zero!");
                srs->state = regState_FetchingZoneData;
                if (srs->nta) CancelGetZoneData(m, srs->nta); // Make sure we cancel old one before we start a new one
-               srs->nta = StartGetZoneData(m, srs->RR_SRV.resrec.name, ZoneServiceUpdate, ServiceRegistrationZoneDataComplete, srs);
+               srs->nta = StartGetZoneData(m, srs->RR_SRV.resrec.name, ZoneServiceUpdate, ServiceRegistrationGotZoneData, srs);
                }
        mDNS_Unlock(m);
        }
@@ -2512,47 +2511,39 @@ mDNSlocal void StartSRVNatMap(mDNS *m, ServiceRecordSet *srs)
        {
        mDNSu8 *p = srs->RR_PTR.resrec.name->c;
        if (p[0]) p += 1 + p[0];
-
        if      (SameDomainLabel(p, (mDNSu8 *)"\x4" "_tcp")) srs->NATinfo.Protocol = NATOp_MapTCP;
        else if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_udp")) srs->NATinfo.Protocol = NATOp_MapUDP;
-       else { LogMsg("StartSRVNatMap: could not determine transport protocol of service %##s", srs->RR_SRV.resrec.name->c); goto error; }
+       else { LogMsg("StartSRVNatMap: could not determine transport protocol of service %##s", srs->RR_SRV.resrec.name->c); return; }
+       
+       if (srs->NATinfo.clientContext) mDNS_StopNATOperation_internal(m, &srs->NATinfo);
        srs->NATinfo.IntPort        = srs->RR_SRV.resrec.rdata->u.srv.port;
        srs->NATinfo.RequestedPort  = srs->RR_SRV.resrec.rdata->u.srv.port;
        srs->NATinfo.NATLease       = 0;                // Request default lease
        srs->NATinfo.clientCallback = CompleteSRVNatMap;
        srs->NATinfo.clientContext  = srs;
        mDNS_StartNATOperation_internal(m, &srs->NATinfo);
-       return;
-
-       error:
-       if (srs->nta) CancelGetZoneData(m, srs->nta); // Make sure we cancel old one before we start a new one
-       srs->nta = StartGetZoneData(m, srs->RR_SRV.resrec.name, ZoneServiceUpdate, ServiceRegistrationZoneDataComplete, srs);
        }
 
 // Called in normal callback context (i.e. mDNS_busy and mDNS_reentrancy are both 1)
-mDNSexport void ServiceRegistrationZoneDataComplete(mDNS *const m, mStatus err, const ZoneData *zoneData)
+mDNSexport void ServiceRegistrationGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneData)
        {
        ServiceRecordSet *srs = (ServiceRecordSet *)zoneData->ZoneDataContext;
        
        if (m->mDNS_busy != m->mDNS_reentrancy)
-               LogMsg("ServiceRegistrationZoneDataComplete: mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+               LogMsg("ServiceRegistrationGotZoneData: mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
 
        srs->nta = mDNSNULL;
 
-       if (err) goto error;
-       if (!zoneData) { LogMsg("ERROR: ServiceRegistrationZoneDataComplete invoked with NULL result and no error"); goto error; }
-
-       if (srs->RR_SRV.resrec.rrclass != zoneData->ZoneClass)
-               { LogMsg("Service %##s - class does not match zone", srs->RR_SRV.resrec.name->c); goto error; }
+       if (err || !zoneData) return;
 
        // cache zone data
        AssignDomainName(&srs->zone, &zoneData->ZoneName);
-       srs->ns.type = mDNSAddrType_IPv4;
-       srs->ns = zoneData->Addr;
+       srs->SRSUpdateServer.type = mDNSAddrType_IPv4;
+       srs->SRSUpdateServer      = zoneData->Addr;
        if (!mDNSIPPortIsZero(zoneData->Port))
                {
                srs->SRSUpdatePort = zoneData->Port;
-               srs->Private = zoneData->ZonePrivate;
+               srs->Private       = zoneData->ZonePrivate;
                }
        else
                {
@@ -2561,14 +2552,17 @@ mDNSexport void ServiceRegistrationZoneDataComplete(mDNS *const m, mStatus err,
                srs->srs_uselease = mDNSfalse;
                }
 
-       LogOperation("ServiceRegistrationZoneDataComplete %#a %d %#a %d", &m->AdvertisedV4, mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4), &srs->ns, mDNSAddrIsRFC1918(&srs->ns));
+       LogOperation("ServiceRegistrationGotZoneData My IPv4 %#a%s Server %#a:%d%s for %##s",
+               &m->AdvertisedV4, mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) ? " (RFC1918)" : "",
+               &srs->SRSUpdateServer, mDNSVal16(srs->SRSUpdatePort), mDNSAddrIsRFC1918(&srs->SRSUpdateServer) ? " (RFC1918)" : "",
+               srs->RR_SRV.resrec.name->c);
 
        if (!mDNSIPPortIsZero(srs->RR_SRV.resrec.rdata->u.srv.port) &&
-               mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) && !mDNSAddrIsRFC1918(&srs->ns) &&
+               mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) && !mDNSAddrIsRFC1918(&srs->SRSUpdateServer) &&
                srs->RR_SRV.AutoTarget == Target_AutoHostAndNATMAP)
                {
                srs->state = regState_NATMap;
-               LogOperation("ServiceRegistrationZoneDataComplete StartSRVNatMap");
+               LogOperation("ServiceRegistrationGotZoneData StartSRVNatMap");
                StartSRVNatMap(m, srs);
                }
        else
@@ -2577,17 +2571,6 @@ mDNSexport void ServiceRegistrationZoneDataComplete(mDNS *const m, mStatus err,
                SendServiceRegistration(m, srs);
                mDNS_Unlock(m);
                }
-       return;
-
-error:
-       unlinkSRS(m, srs);
-       srs->state = regState_Unregistered;
-
-       // Don't need to do the mDNS_DropLockBeforeCallback stuff here, because this code is
-       // *already* being invoked in the right callback context, with mDNS_reentrancy correctly incremented.
-       srs->ServiceCallback(m, srs, err);
-       // CAUTION: MUST NOT do anything more with rr after calling srs->ServiceCallback(), because the client's callback function
-       // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
        }
 
 mDNSlocal void SendServiceDeregistration(mDNS *m, ServiceRecordSet *srs)
@@ -2598,6 +2581,14 @@ mDNSlocal void SendServiceDeregistration(mDNS *m, ServiceRecordSet *srs)
        mStatus err = mStatus_UnknownErr;
        mDNSu32 i;
 
+       if (mDNSIPv4AddressIsZero(srs->SRSUpdateServer.ip.v4))  // Don't know our UpdateServer yet
+               {
+               srs->RR_SRV.LastAPTime = m->timenow;
+               if (srs->RR_SRV.ThisAPInterval < mDNSPlatformOneSecond * 5)
+                       srs->RR_SRV.ThisAPInterval = mDNSPlatformOneSecond * 5;
+               return;
+               }
+
        id = mDNS_NewMessageID(m);
        InitializeDNSMessage(&m->omsg.h, id, UpdateReqFlags);
 
@@ -2615,25 +2606,26 @@ mDNSlocal void SendServiceDeregistration(mDNS *m, ServiceRecordSet *srs)
 
        if (srs->Private)
                {
-               LogOperation("SendServiceDeregistration TCP %p %s", srs->tcp, ARDisplayString(m, &srs->RR_SRV));
-               if (srs->tcp) LogMsg("SendServiceDeregistration: Already have TCP connection for %s", ARDisplayString(m, &srs->RR_SRV));
-               else srs->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &srs->ns, srs->SRSUpdatePort, mDNSNULL, srs, mDNSNULL);
-               srs->RR_SRV.LastAPTime = m->timenow;
-               srs->RR_SRV.ThisAPInterval = 0x3FFFFFFF; // TCP will handle any necessary retransmissions for us
+               if (srs->tcp) LogOperation("SendServiceDeregistration: Disposing existing TCP connection for %s", ARDisplayString(m, &srs->RR_SRV));
+               if (srs->tcp) DisposeTCPConn(srs->tcp);
+               srs->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &srs->SRSUpdateServer, srs->SRSUpdatePort, mDNSNULL, srs, mDNSNULL);
+               if (!srs->tcp) srs->RR_SRV.ThisAPInterval = mDNSPlatformOneSecond * 5; // If failed to make TCP connection, try again in ten seconds (5*2)
+               else if (srs->RR_SRV.ThisAPInterval < mDNSPlatformOneSecond * 30) srs->RR_SRV.ThisAPInterval = mDNSPlatformOneSecond * 30;
                }
        else
                {
-               err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, &srs->ns, srs->SRSUpdatePort, mDNSNULL, GetAuthInfoForName_internal(m, srs->RR_SRV.resrec.name));
+               err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, &srs->SRSUpdateServer, srs->SRSUpdatePort, mDNSNULL, GetAuthInfoForName_internal(m, srs->RR_SRV.resrec.name));
                if (err && err != mStatus_TransientErr) { debugf("ERROR: SendServiceDeregistration - mDNSSendDNSMessage - %ld", err); goto exit; }
-               SetRecordRetry(m, &srs->RR_SRV, err);
                }
 
-       err = mStatus_NoError;
+       SetRecordRetry(m, &srs->RR_SRV, err);
+       return;
 
 exit:
 
        if (err)
                {
+               LogMsg("SendServiceDeregistration ERROR formatting message %d!! Permanently abandoning service registration %##s", err, srs->RR_SRV.resrec.name->c);
                unlinkSRS(m, srs);
                srs->state = regState_Unregistered;
                }
@@ -2653,7 +2645,7 @@ mDNSlocal void UpdateSRV(mDNS *m, ServiceRecordSet *srs)
        const domainname *const nt = GetServiceTarget(m, srs);
        const domainname *const newtarget = nt ? nt : (domainname*)"";
        mDNSBool TargetChanged = (newtarget->c[0] && srs->state == regState_NoTarget) || !SameDomainName(curtarget, newtarget);
-       mDNSBool HaveZoneData  = !mDNSIPv4AddressIsZero(srs->ns.ip.v4);
+       mDNSBool HaveZoneData  = !mDNSIPv4AddressIsZero(srs->SRSUpdateServer.ip.v4);
 
        // Nat state change if:
        // We were behind a NAT, and now we are behind a new NAT, or
@@ -2661,14 +2653,14 @@ mDNSlocal void UpdateSRV(mDNS *m, ServiceRecordSet *srs)
        // We were not behind a NAT and now we are
 
        mDNSIPPort port        = srs->RR_SRV.resrec.rdata->u.srv.port;
-       mDNSBool NowBehindNAT  = (!mDNSIPPortIsZero(port) && mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) && !mDNSAddrIsRFC1918(&srs->ns));
+       mDNSBool NowNeedNATMAP = (srs->RR_SRV.AutoTarget == Target_AutoHostAndNATMAP && !mDNSIPPortIsZero(port) && mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) && !mDNSAddrIsRFC1918(&srs->SRSUpdateServer));
        mDNSBool WereBehindNAT = (srs->NATinfo.clientContext != mDNSNULL);
        mDNSBool PortWasMapped = (srs->NATinfo.clientContext && !mDNSSameIPPort(srs->NATinfo.RequestedPort, port));             // I think this is always false -- SC Sept 07
-       mDNSBool NATChanged    = (!WereBehindNAT && NowBehindNAT) || (!NowBehindNAT && PortWasMapped);
+       mDNSBool NATChanged    = (!WereBehindNAT && NowNeedNATMAP) || (!NowNeedNATMAP && PortWasMapped);
 
-       LogOperation("UpdateSRV %##s newtarget %##s TargetChanged %d HaveZoneData %d port %d NowBehindNAT %d WereBehindNAT %d PortWasMapped %d NATChanged %d",
+       LogOperation("UpdateSRV %##s newtarget %##s TargetChanged %d HaveZoneData %d port %d NowNeedNATMAP %d WereBehindNAT %d PortWasMapped %d NATChanged %d",
                srs->RR_SRV.resrec.name->c, newtarget,
-               TargetChanged, HaveZoneData, mDNSVal16(port), NowBehindNAT, WereBehindNAT, PortWasMapped, NATChanged);
+               TargetChanged, HaveZoneData, mDNSVal16(port), NowNeedNATMAP, WereBehindNAT, PortWasMapped, NATChanged);
 
        if (m->mDNS_busy != m->mDNS_reentrancy+1)
                LogMsg("UpdateSRV: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
@@ -2701,21 +2693,21 @@ mDNSlocal void UpdateSRV(mDNS *m, ServiceRecordSet *srs)
                case regState_NoTarget:
                        if (newtarget->c[0])
                                {
-                               debugf("UpdateSRV: %s service %##s", HaveZoneData ? (NATChanged && NowBehindNAT ? "Starting Port Map for" : "Registering") : "Getting Zone Data for", srs->RR_SRV.resrec.name->c);
+                               debugf("UpdateSRV: %s service %##s", HaveZoneData ? (NATChanged && NowNeedNATMAP ? "Starting Port Map for" : "Registering") : "Getting Zone Data for", srs->RR_SRV.resrec.name->c);
                                if (!HaveZoneData)
                                        {
                                        srs->state = regState_FetchingZoneData;
                                        if (srs->nta) CancelGetZoneData(m, srs->nta); // Make sure we cancel old one before we start a new one
-                                       srs->nta = StartGetZoneData(m, srs->RR_SRV.resrec.name, ZoneServiceUpdate, ServiceRegistrationZoneDataComplete, srs);
+                                       srs->nta = StartGetZoneData(m, srs->RR_SRV.resrec.name, ZoneServiceUpdate, ServiceRegistrationGotZoneData, srs);
                                        }
                                else
                                        {
-                                       if (srs->NATinfo.clientContext && (NATChanged || !NowBehindNAT))
+                                       if (srs->NATinfo.clientContext && (NATChanged || !NowNeedNATMAP))
                                                {
                                                mDNS_StopNATOperation_internal(m, &srs->NATinfo);
                                                srs->NATinfo.clientContext = mDNSNULL;
                                                }
-                                       if (NATChanged && NowBehindNAT && srs->RR_SRV.AutoTarget == Target_AutoHostAndNATMAP)
+                                       if (NATChanged && NowNeedNATMAP && srs->RR_SRV.AutoTarget == Target_AutoHostAndNATMAP)
                                                { srs->state = regState_NATMap; StartSRVNatMap(m, srs); }
                                        else SendServiceRegistration(m, srs);
                                        }
@@ -3189,6 +3181,14 @@ mDNSlocal void SendRecordRegistration(mDNS *const m, AuthRecord *rr)
        if (m->mDNS_busy != m->mDNS_reentrancy+1)
                LogMsg("SendRecordRegistration: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
 
+       if (mDNSIPv4AddressIsZero(rr->UpdateServer.ip.v4))      // Don't know our UpdateServer yet
+               {
+               rr->LastAPTime = m->timenow;
+               if (rr->ThisAPInterval < mDNSPlatformOneSecond * 5)
+                       rr->ThisAPInterval = mDNSPlatformOneSecond * 5;
+               return;
+               }
+
        rr->RequireGoodbye = mDNStrue;
        rr->id = mDNS_NewMessageID(m);
        InitializeDNSMessage(&m->omsg.h, rr->id, UpdateReqFlags);
@@ -3235,42 +3235,27 @@ mDNSlocal void SendRecordRegistration(mDNS *const m, AuthRecord *rr)
        if (rr->Private)
                {
                LogOperation("SendRecordRegistration TCP %p %s", rr->tcp, ARDisplayString(m, rr));
-               if (rr->tcp) LogMsg("SendRecordRegistration: Already have TCP connection for %s", ARDisplayString(m, rr));
-               else rr->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &rr->UpdateServer, rr->UpdatePort, mDNSNULL, mDNSNULL, rr);
-               rr->LastAPTime = m->timenow;
-               rr->ThisAPInterval = 0x3FFFFFFF; // TCP will handle any necessary retransmissions for us
+               if (rr->tcp) LogOperation("SendRecordRegistration: Disposing existing TCP connection for %s", ARDisplayString(m, rr));
+               if (rr->tcp) DisposeTCPConn(rr->tcp);
+               rr->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &rr->UpdateServer, rr->UpdatePort, mDNSNULL, mDNSNULL, rr);
+               if (!rr->tcp) rr->ThisAPInterval = mDNSPlatformOneSecond * 5; // If failed to make TCP connection, try again in ten seconds (5*2)
+               else if (rr->ThisAPInterval < mDNSPlatformOneSecond * 30) rr->ThisAPInterval = mDNSPlatformOneSecond * 30;
                }
        else
                {
                err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, &rr->UpdateServer, rr->UpdatePort, mDNSNULL, GetAuthInfoForName_internal(m, rr->resrec.name));
                if (err) debugf("ERROR: SendRecordRegistration - mDNSSendDNSMessage - %ld", err);
-               SetRecordRetry(m, rr, err);
                }
 
+       SetRecordRetry(m, rr, err);
+
        if (rr->state != regState_Refresh && rr->state != regState_DeregDeferred && rr->state != regState_UpdatePending)
                rr->state = regState_Pending;
 
-       err = mStatus_NoError;
+       return;
 
 exit:
-
-       if (err)
-               {
-               LogMsg("SendRecordRegistration: Error formatting message %d", err);
-
-               if (rr->state != regState_Unregistered)
-                       {
-                       UnlinkAuthRecord(m, rr);
-                       rr->state = regState_Unregistered;
-                       }
-
-               mDNS_DropLockBeforeCallback();
-               if (rr->RecordCallback)
-                       rr->RecordCallback(m, rr, err);
-               mDNS_ReclaimLockAfterCallback();
-               // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function
-               // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
-               }
+       LogMsg("SendRecordRegistration: Error formatting message for %s", ARDisplayString(m, rr));
        }
 
 // Called with lock held
@@ -3283,6 +3268,8 @@ mDNSlocal void hndlServiceUpdateReply(mDNS *const m, ServiceRecordSet *srs,  mSt
        if (m->mDNS_busy != m->mDNS_reentrancy+1)
                LogMsg("hndlServiceUpdateReply: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
 
+       SetRecordRetry(m, &srs->RR_SRV, mStatus_NoError);
+
        switch (srs->state)
                {
                case regState_Pending:
@@ -3297,8 +3284,7 @@ mDNSlocal void hndlServiceUpdateReply(mDNS *const m, ServiceRecordSet *srs,  mSt
                                {
                                srs->TestForSelfConflict = mDNSfalse;
                                if (err == mStatus_NoSuchRecord) err = mStatus_NameConflict;    // NoSuchRecord implies that our prereq was not met, so we actually have a name conflict
-                               if (err) srs->state = regState_Unregistered;
-                               else srs->state = regState_Registered;
+                               if (!err) srs->state = regState_Registered;
                                InvokeCallback = mDNStrue;
                                break;
                                }
@@ -3312,7 +3298,7 @@ mDNSlocal void hndlServiceUpdateReply(mDNS *const m, ServiceRecordSet *srs,  mSt
                        else
                                {
                                //!!!KRS make sure all structs will still get cleaned up when client calls DeregisterService with this state
-                               if (err) { LogMsg("Error %ld for registration of service %##s", err, srs->RR_SRV.resrec.name->c); srs->state = regState_Unregistered; }
+                               if (err) LogMsg("Error %ld for registration of service %##s", err, srs->RR_SRV.resrec.name->c);
                                else srs->state = regState_Registered;
                                InvokeCallback = mDNStrue;
                                break;
@@ -3322,7 +3308,6 @@ mDNSlocal void hndlServiceUpdateReply(mDNS *const m, ServiceRecordSet *srs,  mSt
                                {
                                LogMsg("Error %ld for refresh of service %##s", err, srs->RR_SRV.resrec.name->c);
                                InvokeCallback = mDNStrue;
-                               srs->state = regState_Unregistered;
                                }
                        else srs->state = regState_Registered;
                        break;
@@ -3363,7 +3348,6 @@ mDNSlocal void hndlServiceUpdateReply(mDNS *const m, ServiceRecordSet *srs,  mSt
                        if (err)
                                {
                                LogMsg("hndlServiceUpdateReply: error updating TXT record for service %##s", srs->RR_SRV.resrec.name->c);
-                               srs->state = regState_Unregistered;
                                InvokeCallback = mDNStrue;
                                }
                        else
@@ -3410,7 +3394,7 @@ mDNSlocal void hndlServiceUpdateReply(mDNS *const m, ServiceRecordSet *srs,  mSt
                                {
                                // extra resource record queued for this service - copy zone srs and register
                                AssignDomainName(&(*e)->r.zone, &srs->zone);
-                               (*e)->r.UpdateServer    = srs->ns;
+                               (*e)->r.UpdateServer    = srs->SRSUpdateServer;
                                (*e)->r.UpdatePort  = srs->SRSUpdatePort;
                                (*e)->r.uselease = srs->srs_uselease;
                                SendRecordRegistration(m, &(*e)->r);
@@ -3427,8 +3411,13 @@ mDNSlocal void hndlServiceUpdateReply(mDNS *const m, ServiceRecordSet *srs,  mSt
                else e = &(*e)->next;
                }
 
-       srs->RR_SRV.ThisAPInterval = INIT_UCAST_POLL_INTERVAL - 1;      // reset retry delay for future refreshes, dereg, etc.
-       if (srs->state == regState_Unregistered) unlinkSRS(m, srs);
+       if (srs->state == regState_Unregistered)
+               {
+               if (err != mStatus_MemFree)
+                       LogMsg("hndlServiceUpdateReply ERROR! state == regState_Unregistered but err != mStatus_MemFree. Permanently abandoning service registration %##s",
+                               srs->RR_SRV.resrec.name->c);
+               unlinkSRS(m, srs);
+               }
        else if (txt->QueuedRData && srs->state == regState_Registered)
                {
                if (InvokeCallback)
@@ -3468,23 +3457,17 @@ mDNSlocal void hndlRecordUpdateReply(mDNS *m, AuthRecord *rr, mStatus err)
        if (m->mDNS_busy != m->mDNS_reentrancy+1)
                LogMsg("hndlRecordUpdateReply: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
 
+       SetRecordRetry(m, rr, mStatus_NoError);
+
        if (rr->state == regState_UpdatePending)
                {
-               if (err)
-                       {
-                       LogMsg("Update record failed for %##s (err %d)", rr->resrec.name->c, err);
-                       rr->state = regState_Unregistered;
-                       }
-               else
-                       {
-                       debugf("Update record %##s - success", rr->resrec.name->c);
-                       rr->state = regState_Registered;
-                       // deallocate old RData
-                       if (rr->UpdateCallback) rr->UpdateCallback(m, rr, rr->OrigRData);
-                       SetNewRData(&rr->resrec, rr->InFlightRData, rr->InFlightRDLen);
-                       rr->OrigRData = mDNSNULL;
-                       rr->InFlightRData = mDNSNULL;
-                       }
+               if (err) LogMsg("Update record failed for %##s (err %d)", rr->resrec.name->c, err);
+               rr->state = regState_Registered;
+               // deallocate old RData
+               if (rr->UpdateCallback) rr->UpdateCallback(m, rr, rr->OrigRData);
+               SetNewRData(&rr->resrec, rr->InFlightRData, rr->InFlightRDLen);
+               rr->OrigRData = mDNSNULL;
+               rr->InFlightRData = mDNSNULL;
                }
 
        if (rr->state == regState_DeregPending)
@@ -3527,12 +3510,15 @@ mDNSlocal void hndlRecordUpdateReply(mDNS *m, AuthRecord *rr, mStatus err)
                                return;
                                }
                        LogMsg("hndlRecordUpdateReply: Registration of record %##s type %d failed with error %ld", rr->resrec.name->c, rr->resrec.rrtype, err);
-                       rr->state = regState_Unregistered;
+                       return;
                        }
                }
 
-       if (rr->state == regState_Unregistered) UnlinkAuthRecord(m, rr);
-       else rr->ThisAPInterval = INIT_UCAST_POLL_INTERVAL - 1; // reset retry delay for future refreshes, dereg, etc.
+       if (rr->state == regState_Unregistered)         // Should never happen
+               {
+               LogMsg("hndlRecordUpdateReply rr->state == regState_Unregistered %s", ARDisplayString(m, rr));
+               return;
+               }
 
        if (rr->QueuedRData && rr->state == regState_Registered)
                {
@@ -3724,29 +3710,30 @@ mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNS
                {
                //if (srcaddr && recvLLQResponse(m, msg, end, srcaddr, srcport)) return;
                if (uDNS_ReceiveTestQuestionResponse(m, msg, end, srcaddr, srcport)) return;
-               if (!mDNSOpaque16IsZero(msg->h.id))
-                       for (qptr = m->Questions; qptr; qptr = qptr->next)
-                               if (msg->h.flags.b[0] & kDNSFlag0_TC && mDNSSameOpaque16(qptr->TargetQID, msg->h.id) && m->timenow - qptr->LastQTime < RESPONSE_WINDOW)
+               for (qptr = m->Questions; qptr; qptr = qptr->next)
+                       if (msg->h.flags.b[0] & kDNSFlag0_TC && mDNSSameOpaque16(qptr->TargetQID, msg->h.id) && m->timenow - qptr->LastQTime < RESPONSE_WINDOW)
+                               {
+                               if (!srcaddr) LogMsg("uDNS_ReceiveMsg: TCP DNS response had TC bit set: ignoring");
+                               else if (qptr->tcp)
                                        {
-                                       if (!srcaddr) LogMsg("uDNS_ReceiveMsg: TCP DNS response had TC bit set: ignoring");
-                                       else if (qptr->tcp)
-                                               {
-                                               // There may be a race condition here, if the server decides to drop the connection just as we decide to reuse it
-                                               // For now it should not be serious because our normal retry logic (as used to handle UDP packet loss)
-                                               // should take care of it but later we may want to look at handling this case explicitly
-                                               LogOperation("uDNS_ReceiveMsg: Using existing TCP connection for %##s (%s)", qptr->qname.c, DNSTypeName(qptr->qtype));
-                                               mDNS_DropLockBeforeCallback();
-                                               tcpCallback(qptr->tcp->sock, qptr->tcp, mDNStrue, mStatus_NoError);
-                                               mDNS_ReclaimLockAfterCallback();
-                                               }
-                                       else qptr->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_Zero, srcaddr, srcport, qptr, mDNSNULL, mDNSNULL);
+                                       // There may be a race condition here, if the server decides to drop the connection just as we decide to reuse it
+                                       // For now it should not be serious because our normal retry logic (as used to handle UDP packet loss)
+                                       // should take care of it but later we may want to look at handling this case explicitly
+                                       LogOperation("uDNS_ReceiveMsg: Using existing TCP connection for %##s (%s)", qptr->qname.c, DNSTypeName(qptr->qtype));
+                                       mDNS_DropLockBeforeCallback();
+                                       tcpCallback(qptr->tcp->sock, qptr->tcp, mDNStrue, mStatus_NoError);
+                                       mDNS_ReclaimLockAfterCallback();
                                        }
+                               else qptr->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_Zero, srcaddr, srcport, qptr, mDNSNULL, mDNSNULL);
+                               }
                }
 
-       if (QR_OP == UpdateR && !mDNSOpaque16IsZero(msg->h.id))
+       if (QR_OP == UpdateR)
                {
                mDNSu32 lease = GetPktLease(m, msg, end);
-               mDNSs32 expire = (m->timenow + (((mDNSs32)lease * mDNSPlatformOneSecond)) * 3/4);
+               mDNSs32 expire = m->timenow + (mDNSs32)lease * mDNSPlatformOneSecond;
+
+               //rcode = kDNSFlag1_RC_SrvErr;  // Simulate server failure (rcode 2)
 
                if (CurrentServiceRecordSet)
                        LogMsg("uDNS_ReceiveMsg ERROR CurrentServiceRecordSet already set");
@@ -3761,8 +3748,8 @@ mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNS
                                {
                                err = checkUpdateResult(m, sptr->RR_SRV.resrec.name, rcode, msg, end);
                                if (!err && sptr->srs_uselease && lease)
-                                       if (sptr->expire - expire >= 0 || sptr->state != regState_UpdatePending)
-                                               sptr->expire = expire;
+                                       if (sptr->RR_SRV.expire - expire >= 0 || sptr->state != regState_UpdatePending)
+                                               sptr->RR_SRV.expire = expire;
                                hndlServiceUpdateReply(m, sptr, err);
                                CurrentServiceRecordSet = mDNSNULL;
                                return;
@@ -3796,7 +3783,7 @@ mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNS
 #pragma mark - Query Routines
 #endif
 
-mDNSlocal void sendLLQRefresh(mDNS *m, DNSQuestion *q, mDNSu32 lease)
+mDNSexport void sendLLQRefresh(mDNS *m, DNSQuestion *q, mDNSu32 lease)
        {
        mDNSu8 *end;
        LLQOptData llq;
@@ -3816,20 +3803,16 @@ mDNSlocal void sendLLQRefresh(mDNS *m, DNSQuestion *q, mDNSu32 lease)
                return;
                }
 
-       if ((q->state == LLQ_Refresh && q->ntries >= kLLQ_MAX_TRIES) || q->expire - m->timenow < 0)
+       if ((q->state == LLQ_Established && q->ntries >= kLLQ_MAX_TRIES) || q->expire - m->timenow < 0)
                {
-               LogMsg("Unable to refresh LLQ %##s (%s) - will retry in %d minutes", q->qname.c, DNSTypeName(q->qtype), kLLQ_DEF_RETRY/60);
-               q->state         = LLQ_Retry;
-               q->LastQTime     = m->timenow;
-               q->ThisQInterval = kLLQ_DEF_RETRY * mDNSPlatformOneSecond;
-               SetNextQueryTime(m, q);
+               LogMsg("Unable to refresh LLQ %##s (%s) - will retry in %d minutes", q->qname.c, DNSTypeName(q->qtype), LLQ_POLL_INTERVAL/3600);
+               StartLLQPolling(m,q);
                return;
-               //!!!KRS handle this - periodically try to re-establish
                }
 
        llq.vers  = kLLQ_Vers;
        llq.llqOp = kLLQOp_Refresh;
-       llq.err   = LLQErr_NoError;
+       llq.err   = q->tcp ? GetLLQEventPort(m, &q->servAddr) : LLQErr_NoError; // If using TCP tell server what UDP port to send notifications to
        llq.id    = q->id;
        llq.llqlease = lease;
 
@@ -3840,89 +3823,48 @@ mDNSlocal void sendLLQRefresh(mDNS *m, DNSQuestion *q, mDNSu32 lease)
        err = mDNSSendDNSMessage(m, &m->omsg, end, mDNSInterface_Any, &q->servAddr, q->servPort, q->tcp ? q->tcp->sock : mDNSNULL, q->AuthInfo);
        if (err) debugf("ERROR: sendLLQRefresh - mDNSSendDNSMessage returned %ld", err);
 
-       if (q->state == LLQ_Established) q->ntries = 1;
-       else q->ntries++;
+       q->ntries++;
 
        debugf("sendLLQRefresh ntries %d %##s (%s)", q->ntries, q->qname.c, DNSTypeName(q->qtype));
 
-       q->state = LLQ_Refresh;
        q->LastQTime = m->timenow;
        SetNextQueryTime(m, q);
        }
 
-// wrapper for startLLQHandshake, invoked by async op callback
-mDNSexport void startLLQHandshakeCallback(mDNS *const m, mStatus err, const ZoneData *zoneInfo)
+mDNSexport void LLQGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo)
        {
        DNSQuestion *q = (DNSQuestion *)zoneInfo->ZoneDataContext;
 
+       mDNS_Lock(m);
+
        // If we get here it means that the GetZoneData operation has completed, and is is about to cancel
        // its question and free the ZoneData memory. We no longer need to hold onto our pointer (which
        // we use for cleaning up if our LLQ is cancelled *before* the GetZoneData operation has completes).
-       q->nta = mDNSNULL;
+       q->nta      = mDNSNULL;
+       q->servAddr = zeroAddr;
+       q->servPort = zeroIPPort;
 
-       // check state first to make sure it is OK to touch question object
-       if (q->state == LLQ_Cancelled)
+       if (!err && zoneInfo && !mDNSIPPortIsZero(zoneInfo->Port))
                {
-               // StopQuery was called while we were getting the zone info
-               debugf("startLLQHandshakeCallback - LLQ Cancelled.");
-               return;
+               q->servAddr = zoneInfo->Addr;
+               q->servPort = zoneInfo->Port;
+               q->AuthInfo = zoneInfo->ZonePrivate ? GetAuthInfoForName_internal(m, &q->qname) : mDNSNULL;
+               q->ntries = 0;
+               LogOperation("LLQGotZoneData %#a:%d", &q->servAddr, mDNSVal16(q->servPort));
+               startLLQHandshake(m, q);
                }
-
-       mDNS_Lock(m);
-
-       if (q->state != LLQ_GetZoneInfo)
-               {
-               LogMsg("ERROR: startLLQHandshakeCallback - bad state %d", q->state);
-               err = mStatus_UnknownErr;
-               goto exit;
-               }
-
-       if (err)
-               {
-               LogMsg("ERROR: startLLQHandshakeCallback %##s (%s) invoked with error code %ld", q->qname.c, DNSTypeName(q->qtype), err);
-               StartLLQPolling(m, q);
-               err = mStatus_NoError;
-               goto exit;
-               }
-
-       if (!zoneInfo)
-               {
-               LogMsg("ERROR: startLLQHandshakeCallback invoked with NULL result and no error code");
-               err = mStatus_UnknownErr;
-               goto exit;
-               }
-
-       if (mDNSIPPortIsZero(zoneInfo->Port))
-               {
-               LogOperation("LLQ port lookup failed - reverting to polling for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
-               q->servPort = zeroIPPort;
-               StartLLQPolling(m, q);
-               goto exit;
-               }
-
-       // cache necessary zone data
-       q->servAddr  = zoneInfo->Addr;
-       q->servPort  = zoneInfo->Port;
-       if (!zoneInfo->ZonePrivate) q->AuthInfo = mDNSNULL;
-
-       q->ntries = 0;
-
-       if (q->state == LLQ_SuspendDeferred) q->state = LLQ_Suspended;
-       else startLLQHandshake(m, q);
-
-exit:
-
-       if (err && q) q->state = LLQ_Error;
+       else
+               StartLLQPolling(m,q);
 
        mDNS_Unlock(m);
        }
 
 // Called in normal callback context (i.e. mDNS_busy and mDNS_reentrancy are both 1)
-mDNSlocal void startPrivateQueryCallback(mDNS *const m, mStatus err, const ZoneData *zoneInfo)
+mDNSlocal void PrivateQueryGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo)
        {
        DNSQuestion *q = (DNSQuestion *) zoneInfo->ZoneDataContext;
 
-       LogOperation("startPrivateQueryCallback %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+       LogOperation("PrivateQueryGotZoneData %##s (%s) err %d Zone %##s Private %d", q->qname.c, DNSTypeName(q->qtype), err, zoneInfo->ZoneName.c, zoneInfo->ZonePrivate);
 
        // If we get here it means that the GetZoneData operation has completed, and is is about to cancel
        // its question and free the ZoneData memory. We no longer need to hold onto our pointer (which
@@ -3931,15 +3873,8 @@ mDNSlocal void startPrivateQueryCallback(mDNS *const m, mStatus err, const ZoneD
 
        if (err)
                {
-               LogMsg("ERROR: startPrivateQueryCallback %##s (%s) invoked with error code %ld", q->qname.c, DNSTypeName(q->qtype), err);
-               goto exit;
-               }
-
-       if (!zoneInfo)
-               {
-               LogMsg("ERROR: startPrivateQueryCallback invoked with NULL result and no error code");
-               err = mStatus_UnknownErr;
-               goto exit;
+               LogMsg("ERROR: PrivateQueryGotZoneData %##s (%s) invoked with error code %ld", q->qname.c, DNSTypeName(q->qtype), err);
+               return;
                }
 
        if (!zoneInfo->ZonePrivate)
@@ -3951,50 +3886,19 @@ mDNSlocal void startPrivateQueryCallback(mDNS *const m, mStatus err, const ZoneD
                mDNS_Lock(m);
                SetNextQueryTime(m, q);
                mDNS_Unlock(m);
-               goto exit;
+               return;
                // Next call to uDNS_CheckCurrentQuestion() will do this as a non-private query
                }
 
        if (!q->AuthInfo)
                {
-               LogMsg("ERROR: startPrivateQueryCallback: cannot find credentials for q %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
-               err = mStatus_UnknownErr;
-               goto exit;
+               LogMsg("ERROR: PrivateQueryGotZoneData: cannot find credentials for q %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+               return;
                }
 
        q->TargetQID = mDNS_NewMessageID(m);
-       if (q->tcp)
-               {
-               LogOperation("startPrivateQueryCallback: Already have TCP connection for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
-               tcpCallback(q->tcp->sock, q->tcp, mDNStrue, mStatus_NoError);
-               }
-       else q->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_UseTLS, &zoneInfo->Addr, zoneInfo->Port, q, mDNSNULL, mDNSNULL);
-
-exit:
-
-       if (err) mDNS_StopQuery(m, q);
-       }
-
-// uDNS_StopLongLivedQuery happens IN ADDITION to stopQuery
-mDNSexport void uDNS_StopLongLivedQuery(mDNS *const m, DNSQuestion *const question)
-       {
-       LogOperation("uDNS_StopLongLivedQuery %##s (%s) state %d", question->qname.c, DNSTypeName(question->qtype), question->state);
-
-       switch (question->state)
-               {
-               case LLQ_UnInit: LogMsg("ERROR: uDNS_StopLongLivedQuery - state LLQ_UnInit"); return; //!!!KRS should we unlink info<->question here?
-
-               case LLQ_GetZoneInfo:
-               case LLQ_SuspendDeferred: question->state = LLQ_Cancelled; return;
-
-               case LLQ_Established:
-               case LLQ_Refresh: sendLLQRefresh(m, question, 0); break;
-
-               default: debugf("uDNS_StopLongLivedQuery - silently discarding LLQ in state %d", question->state); break;
-               }
-
-       RemoveLLQNatMappings(m, question);
-       CheckForUnreferencedLLQMapping(m);
+       if (q->tcp) DisposeTCPConn(q->tcp);
+       q->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_UseTLS, &zoneInfo->Addr, zoneInfo->Port, q, mDNSNULL, mDNSNULL);
        }
 
 // ***************************************************************************
@@ -4003,29 +3907,33 @@ mDNSexport void uDNS_StopLongLivedQuery(mDNS *const m, DNSQuestion *const questi
 #endif
 
 // Called in normal callback context (i.e. mDNS_busy and mDNS_reentrancy are both 1)
-mDNSexport void RecordRegistrationCallback(mDNS *const m, mStatus err, const ZoneData *zoneData)
+mDNSexport void RecordRegistrationGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneData)
        {
        AuthRecord *newRR = (AuthRecord*)zoneData->ZoneDataContext;
        AuthRecord *ptr;
 
        if (m->mDNS_busy != m->mDNS_reentrancy)
-               LogMsg("RecordRegistrationCallback: mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+               LogMsg("RecordRegistrationGotZoneData: mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
 
        newRR->nta = mDNSNULL;
 
        // make sure record is still in list (!!!)
        for (ptr = m->ResourceRecords; ptr; ptr = ptr->next) if (ptr == newRR) break;
-       if (!ptr) { LogMsg("RecordRegistrationCallback - RR no longer in list.  Discarding."); return; }
+       if (!ptr) { LogMsg("RecordRegistrationGotZoneData - RR no longer in list.  Discarding."); return; }
 
        // check error/result
-       if (err) { LogMsg("RecordRegistrationCallback: error %ld", err); goto error; }
-       if (!zoneData) { LogMsg("ERROR: RecordRegistrationCallback invoked with NULL result and no error"); goto error; }
+       if (err)
+               {
+               if (err != mStatus_NoSuchNameErr) LogMsg("RecordRegistrationGotZoneData: error %ld", err);
+               return;
+               }
+
+       if (!zoneData) { LogMsg("ERROR: RecordRegistrationGotZoneData invoked with NULL result and no error"); return; }
 
        if (newRR->resrec.rrclass != zoneData->ZoneClass)
                {
-               LogMsg("ERROR: New resource record's class (%d) does not match zone class (%d)",
-                          newRR->resrec.rrclass, zoneData->ZoneClass);
-               goto error;
+               LogMsg("ERROR: New resource record's class (%d) does not match zone class (%d)", newRR->resrec.rrclass, zoneData->ZoneClass);
+               return;
                }
 
        // Don't try to do updates to the root name server.
@@ -4033,9 +3941,8 @@ mDNSexport void RecordRegistrationCallback(mDNS *const m, mStatus err, const Zon
        // organizations use their own private pseudo-TLD, like ".home", etc, and we don't want to block that.
        if (zoneData->ZoneName.c[0] == 0)
                {
-               LogMsg("RecordRegistrationCallback: Only name server claiming responsibility for \"%##s\" is \"%##s\"!", newRR->resrec.name->c, zoneData->ZoneName.c);
-               err = mStatus_NoSuchNameErr;
-               goto error;
+               LogOperation("RecordRegistrationGotZoneData: No name server found claiming responsibility for \"%##s\"!", newRR->resrec.name->c);
+               return;
                }
 
        // Store discovered zone data
@@ -4043,79 +3950,60 @@ mDNSexport void RecordRegistrationCallback(mDNS *const m, mStatus err, const Zon
        newRR->UpdateServer = zoneData->Addr;
        newRR->UpdatePort   = zoneData->Port;
        newRR->Private      = zoneData->ZonePrivate;
-       debugf("RecordRegistrationCallback: Set newRR->UpdateServer %##s %##s to %#a:%d",
+       debugf("RecordRegistrationGotZoneData: Set newRR->UpdateServer %##s %##s to %#a:%d",
                newRR->resrec.name->c, zoneData->ZoneName.c, &newRR->UpdateServer, mDNSVal16(newRR->UpdatePort));
 
        if (mDNSIPPortIsZero(zoneData->Port) || mDNSAddressIsZero(&zoneData->Addr))
                {
-               LogMsg("RecordRegistrationCallback: No _dns-update._udp service found for \"%##s\"!", newRR->resrec.name->c);
-               err = mStatus_NoSuchNameErr;
-               goto error;
+               LogOperation("RecordRegistrationGotZoneData: No _dns-update._udp service found for \"%##s\"!", newRR->resrec.name->c);
+               return;
                }
 
-
        mDNS_Lock(m);   // SendRecordRegistration expects to be called with the lock held
        SendRecordRegistration(m, newRR);
        mDNS_Unlock(m);
-       return;
-
-error:
-       if (newRR->state != regState_Unregistered)
-               {
-               mDNS_Lock(m);
-               UnlinkAuthRecord(m, newRR);
-               newRR->state = regState_Unregistered;
-               mDNS_Unlock(m);
-               }
-
-       // Don't need to do the mDNS_DropLockBeforeCallback stuff here, because this code is
-       // *already* being invoked in the right callback context, with mDNS_reentrancy correctly incremented.
-       if (newRR->RecordCallback)
-               newRR->RecordCallback(m, newRR, err);
-       // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function
-       // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
        }
 
 mDNSlocal void SendRecordDeregistration(mDNS *m, AuthRecord *rr)
        {
        mDNSu8 *ptr = m->omsg.data;
        mDNSu8 *end = (mDNSu8 *)&m->omsg + sizeof(DNSMessage);
-       mStatus err = mStatus_NoError;
-
-       InitializeDNSMessage(&m->omsg.h, rr->id, UpdateReqFlags);
 
-       ptr = putZone(&m->omsg, ptr, end, &rr->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass));
-       if (!ptr) { err = mStatus_UnknownErr; goto exit; }
-       if (!(ptr = putDeletionRecord(&m->omsg, ptr, &rr->resrec))) { err = mStatus_UnknownErr; goto exit; }
-
-       rr->state = regState_DeregPending;
-
-       if (rr->Private)
+       if (mDNSIPv4AddressIsZero(rr->UpdateServer.ip.v4))      // Don't know our UpdateServer yet
                {
-               LogOperation("SendRecordDeregistration TCP %p %s", rr->tcp, ARDisplayString(m, rr));
-               if (rr->tcp) LogMsg("SendRecordDeregistration: ERROR: Already have TCP connection for %s", ARDisplayString(m, rr));
-               else rr->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &rr->UpdateServer, rr->UpdatePort, mDNSNULL, mDNSNULL, rr);
                rr->LastAPTime = m->timenow;
-               rr->ThisAPInterval = 0x3FFFFFFF; // TCP will handle any necessary retransmissions for us
-               }
-       else
-               {
-               err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, &rr->UpdateServer, rr->UpdatePort, mDNSNULL, GetAuthInfoForName_internal(m, rr->resrec.name));
-               if (err) debugf("ERROR: SendRecordDeregistration - mDNSSendDNSMessage - %ld", err);
-               SetRecordRetry(m, rr, err);
-               CompleteDeregistration(m, rr);          // Don't touch rr after this
+               if (rr->ThisAPInterval < mDNSPlatformOneSecond * 5)
+                       rr->ThisAPInterval = mDNSPlatformOneSecond * 5;
                return;
                }
 
-       err = mStatus_NoError;
-
-exit:
+       InitializeDNSMessage(&m->omsg.h, rr->id, UpdateReqFlags);
 
-       if (err)
+       ptr = putZone(&m->omsg, ptr, end, &rr->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass));
+       if (ptr) ptr = putDeletionRecord(&m->omsg, ptr, &rr->resrec);
+       if (!ptr)
                {
-               LogMsg("Error: SendRecordDeregistration - could not contruct deregistration packet");
-               UnlinkAuthRecord(m, rr);
-               rr->state = regState_Unregistered;
+               LogMsg("SendRecordDeregistration Error: could not contruct deregistration packet for %s", ARDisplayString(m, rr));
+               if (rr->state == regState_DeregPending) CompleteDeregistration(m, rr);
+               }
+       else
+               {
+               if (rr->Private)
+                       {
+                       LogOperation("SendRecordDeregistration TCP %p %s", rr->tcp, ARDisplayString(m, rr));
+                       if (rr->tcp) LogOperation("SendRecordDeregistration: Disposing existing TCP connection for %s", ARDisplayString(m, rr));
+                       if (rr->tcp) DisposeTCPConn(rr->tcp);
+                       rr->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &rr->UpdateServer, rr->UpdatePort, mDNSNULL, mDNSNULL, rr);
+                       if (!rr->tcp) rr->ThisAPInterval = mDNSPlatformOneSecond * 5; // If failed to make TCP connection, try again in ten seconds (5*2)
+                       else if (rr->ThisAPInterval < mDNSPlatformOneSecond * 30) rr->ThisAPInterval = mDNSPlatformOneSecond * 30;
+                       SetRecordRetry(m, rr, mStatus_NoError);
+                       }
+               else
+                       {
+                       mStatus err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, &rr->UpdateServer, rr->UpdatePort, mDNSNULL, GetAuthInfoForName_internal(m, rr->resrec.name));
+                       if (err) debugf("ERROR: SendRecordDeregistration - mDNSSendDNSMessage - %ld", err);
+                       if (rr->state == regState_DeregPending) CompleteDeregistration(m, rr);          // Don't touch rr after this
+                       }
                }
        }
 
@@ -4128,9 +4016,6 @@ mDNSexport mStatus uDNS_DeregisterRecord(mDNS *const m, AuthRecord *const rr)
                case regState_Refresh:
                case regState_Pending:
                case regState_UpdatePending:
-                       rr->state = regState_DeregDeferred;
-                       LogMsg("Deferring deregistration of record %##s until registration completes", rr->resrec.name->c);
-                       return mStatus_NoError;
                case regState_FetchingZoneData:
                case regState_Registered: break;
                case regState_DeregPending: break;
@@ -4141,7 +4026,7 @@ mDNSexport mStatus uDNS_DeregisterRecord(mDNS *const m, AuthRecord *const rr)
                default: LogMsg("uDNS_DeregisterRecord: State %d for %##s type %s", rr->state, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); return mStatus_NoError;
                }
 
-       if (rr->state != regState_Unregistered) SendRecordDeregistration(m, rr);
+       if (rr->state != regState_Unregistered) { rr->state = regState_DeregPending; SendRecordDeregistration(m, rr); }
        return mStatus_NoError;
        }
 
@@ -4169,12 +4054,6 @@ mDNSexport mStatus uDNS_DeregisterService(mDNS *const m, ServiceRecordSet *srs)
                case regState_Unregistered:
                        debugf("uDNS_DeregisterService - service %##s not registered", srs->RR_SRV.resrec.name->c);
                        return mStatus_BadReferenceErr;
-               case regState_Pending:
-               case regState_Refresh:
-               case regState_UpdatePending:
-                       // deregister following completion of in-flight operation
-                       srs->state = regState_DeregDeferred;
-                       return mStatus_NoError;
                case regState_DeregPending:
                case regState_DeregDeferred:
                        debugf("Double deregistration of service %##s", srs->RR_SRV.resrec.name->c);
@@ -4188,6 +4067,9 @@ mDNSexport mStatus uDNS_DeregisterService(mDNS *const m, ServiceRecordSet *srs)
                        srs->ServiceCallback(m, srs, mStatus_MemFree);
                        mDNS_ReclaimLockAfterCallback();
                        return mStatus_NoError;
+               case regState_Pending:
+               case regState_Refresh:
+               case regState_UpdatePending:
                case regState_FetchingZoneData:
                case regState_Registered:
                        srs->state = regState_DeregPending;
@@ -4313,25 +4195,14 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m)
                sendtime = m->SuppressStdPort53Queries;
        if (m->timenow - sendtime < 0) return;
 
-       if (q->LongLived && q->state != LLQ_Poll)
+       if (q->LongLived)
                {
-               if (q->state >= LLQ_InitialRequest && q->state <= LLQ_Established)
+               switch (q->state)
                        {
-                       // sanity check to avoid packet flood bugs
-                       if      (q->state == LLQ_Established || q->state == LLQ_Refresh) sendLLQRefresh(m, q, q->origLease);
-                       else if (q->state == LLQ_InitialRequest                        ) startLLQHandshake(m, q);
-                       else if (q->state == LLQ_SecondaryRequest                      ) sendChallengeResponse(m, q, mDNSNULL);
-                       else if (q->state == LLQ_Retry                                 ) { q->ntries = 0; startLLQHandshake(m, q); }
-                       }
-               else
-                       {
-                       // This should never happen. Any LLQ not in states LLQ_InitialRequest to LLQ_Established should not have have ThisQInterval set.
-                       // (uDNS_CheckCurrentQuestion() is only called for DNSQuestions with non-zero ThisQInterval)
-                       LogMsg("uDNS_CheckCurrentQuestion: %##s (%s) state %d sendtime %d ThisQInterval %d",
-                               q->qname.c, DNSTypeName(q->qtype), q->state, sendtime - m->timenow, q->ThisQInterval);
-                       q->LastQTime = m->timenow;
-                       q->ThisQInterval *= 2;
-                       SetNextQueryTime(m, q);
+                       case LLQ_InitialRequest:   startLLQHandshake(m, q); break;
+                       case LLQ_SecondaryRequest: sendChallengeResponse(m, q, mDNSNULL); break;
+                       case LLQ_Established:      sendLLQRefresh(m, q, q->origLease); break;
+                       case LLQ_Poll:             break;       // Do nothing (handled below)
                        }
                }
 
@@ -4346,52 +4217,43 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m)
 
                        if (q->qDNSServer->teststate != DNSServer_Untested || NoTestQuery(q))
                                {
-                               err = constructQueryMsg(&m->omsg, &end, q);
+                               InitializeDNSMessage(&m->omsg.h, q->TargetQID, uQueryFlags);
+                               end = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass);
                                private = q->AuthInfo;
                                }
                        else if (m->timenow - q->qDNSServer->lasttest >= INIT_UCAST_POLL_INTERVAL)      // Make sure at least three seconds has elapsed since last test query
                                {
                                LogOperation("Sending DNS test query to %#a:%d", &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port));
-                               q->ThisQInterval = INIT_UCAST_POLL_INTERVAL;
+                               q->ThisQInterval = INIT_UCAST_POLL_INTERVAL / QuestionIntervalStep;
                                q->qDNSServer->lasttest = m->timenow;
                                InitializeDNSMessage(&m->omsg.h, mDNS_NewMessageID(m), uQueryFlags);
                                end = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, DNSRelayTestQuestion, kDNSType_PTR, kDNSClass_IN);
                                }
 
-                       if (err) LogMsg("Error: uDNS_CheckCurrentQuestion - constructQueryMsg. Skipping question %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
-                       else
+                       if (end > m->omsg.data && (q->qDNSServer->teststate != DNSServer_Failed || NoTestQuery(q)))
                                {
-                               if (end > m->omsg.data && (q->qDNSServer->teststate != DNSServer_Failed || NoTestQuery(q)))
+                               //LogMsg("uDNS_CheckCurrentQuestion %d %p %##s (%s)", sendtime - m->timenow, private, q->qname.c, DNSTypeName(q->qtype));
+                               if (private)
                                        {
-                                       //LogMsg("uDNS_CheckCurrentQuestion %d %p %##s (%s)", sendtime - m->timenow, private, q->qname.c, DNSTypeName(q->qtype));
-                                       if (private)
-                                               {
-                                               if (q->nta) LogMsg("uDNS_CheckCurrentQuestion Error: GetZoneData already started for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
-                                               else q->nta = StartGetZoneData(m, &q->qname, q->LongLived ? ZoneServiceLLQ : ZoneServiceQuery, startPrivateQueryCallback, q);
-                                               q->ThisQInterval = 0;           // Suspend this question until GetZoneData completes
-                                               }
-                                       else
-                                               {
-                                               err = mDNSSendDNSMessage(m, &m->omsg, end, q->qDNSServer->interface, &q->qDNSServer->addr, q->qDNSServer->port, mDNSNULL, mDNSNULL);
-                                               m->SuppressStdPort53Queries = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+99)/100);
-                                               }
-                                       }
-
-                               if (err) debugf("ERROR: uDNS_idle - mDNSSendDNSMessage - %ld", err); // surpress syslog messages if we have no network
-                               else if (!q->LongLived && q->ThisQInterval < MAX_UCAST_POLL_INTERVAL)
-                                       {
-                                       q->ThisQInterval = q->ThisQInterval * QuestionIntervalStep;     // Only increase interval if send succeeded
-                                       LogOperation("Adjusted ThisQInterval to %d for %##s (%s)", q->ThisQInterval, q->qname.c, DNSTypeName(q->qtype));
+                                       if (q->nta) CancelGetZoneData(m, q->nta);
+                                       q->nta = StartGetZoneData(m, &q->qname, q->LongLived ? ZoneServiceLLQ : ZoneServiceQuery, PrivateQueryGotZoneData, q);
+                                       q->ThisQInterval = (LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10)) / QuestionIntervalStep;
                                        }
-                               else if (q->LongLived && q->state == LLQ_Poll)
+                               else
                                        {
-                                       // Bit of a hack here -- if we dropped the interval down to do the DNS test query, need to put
-                                       // it back or we'll poll every three seconds. The real solution is that the DNS test query
-                                       // should be a real query in its own right, which other queries are blocked on, rather than
-                                       // being shoehorned in here and borrowing another question's q->LastQTime and q->ThisQInterval
-                                       q->ThisQInterval = LLQ_POLL_INTERVAL;
+                                       err = mDNSSendDNSMessage(m, &m->omsg, end, q->qDNSServer->interface, &q->qDNSServer->addr, q->qDNSServer->port, mDNSNULL, mDNSNULL);
+                                       m->SuppressStdPort53Queries = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+99)/100);
                                        }
                                }
+
+                       if (err) debugf("ERROR: uDNS_idle - mDNSSendDNSMessage - %ld", err); // surpress syslog messages if we have no network
+                       else
+                               {
+                               q->ThisQInterval = q->ThisQInterval * QuestionIntervalStep;     // Only increase interval if send succeeded
+                               if (q->ThisQInterval > MAX_UCAST_POLL_INTERVAL)
+                                       q->ThisQInterval = MAX_UCAST_POLL_INTERVAL;
+                               LogOperation("Increased ThisQInterval to %d for %##s (%s)", q->ThisQInterval, q->qname.c, DNSTypeName(q->qtype));
+                               }
                        q->LastQTime = m->timenow;
                        SetNextQueryTime(m, q);
                        }
@@ -4401,7 +4263,10 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m)
                        // a transient failure indication to the client. This is important for things like iPhone
                        // where we want to return timely feedback to the user when no network is available.
                        // After calling MakeNegativeCacheRecord() we store the resulting record in the
-                       // cache so that it will be visible to other clients asking the same question
+                       // cache so that it will be visible to other clients asking the same question.
+                       // (When we have a group of identical questions, only the active representative of the group gets
+                       // passed to uDNS_CheckCurrentQuestion -- we only want one set of query packets hitting the wire --
+                       // but we want *all* of the questions to get answer callbacks.)
 
                        CacheRecord *rr;
                        const mDNSu32 slot = HashSlot(&q->qname);
@@ -4410,7 +4275,7 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m)
                                for (rr = cg->members; rr; rr=rr->next)
                                        if (SameNameRecordAnswersQuestion(&rr->resrec, q)) mDNS_PurgeCacheResourceRecord(m, rr);
 
-                       if (!q->qDNSServer) LogMsg("uDNS_CheckCurrentQuestion no DNS server for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+                       if (!q->qDNSServer) LogOperation("uDNS_CheckCurrentQuestion no DNS server for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
                        else LogMsg("uDNS_CheckCurrentQuestion DNS server %#a:%d for %##s is disabled", &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qname.c);
 
                        MakeNegativeCacheRecord(m, &q->qname, q->qnamehash, q->qtype, q->qclass, 60);
@@ -4523,7 +4388,7 @@ mDNSlocal void CheckNATMappings(mDNS *m)
                                        cur->Result != cur->NewResult)
                                        {
                                        //LogMsg("NAT callback %d %d %d", cur->Protocol, cur->ExpiryTime, cur->retryInterval);
-                                       if (cur->Protocol && mDNSIPPortIsZero(ExternalPort))
+                                       if (cur->Protocol && mDNSIPPortIsZero(ExternalPort) && !mDNSIPv4AddressIsZero(m->Router.ip.v4))
                                                LogMsg("Failed to obtain NAT port mapping from router %#a external address %.4a internal port %d",
                                                        &m->Router, &m->ExternalAddress, mDNSVal16(cur->IntPort));
                                        cur->ExternalAddress = m->ExternalAddress;
@@ -4548,25 +4413,17 @@ mDNSlocal mDNSs32 CheckRecordRegistrations(mDNS *m)
 
        for (rr = m->ResourceRecords; rr; rr = rr->next)
                {
-               if (rr->state == regState_Pending || rr->state == regState_DeregPending || rr->state == regState_UpdatePending || rr->state == regState_DeregDeferred || rr->state == regState_Refresh)
+               if (rr->state == regState_Pending || rr->state == regState_DeregPending || rr->state == regState_UpdatePending ||
+                       rr->state == regState_DeregDeferred || rr->state == regState_Refresh || rr->state == regState_Registered)
                        {
                        if (rr->LastAPTime + rr->ThisAPInterval - m->timenow < 0)
                                {
-                               if (rr->tcp) { rr->LastAPTime = m->timenow; rr->ThisAPInterval = 0x3FFFFFFF; }
-                               else if (rr->state == regState_DeregPending) SendRecordDeregistration(m, rr);
+                               if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; }
+                               if (rr->state == regState_DeregPending) SendRecordDeregistration(m, rr);
                                else SendRecordRegistration(m, rr);
                                }
-                       if (rr->LastAPTime + rr->ThisAPInterval - nextevent < 0) nextevent = rr->LastAPTime + rr->ThisAPInterval;
-                       }
-               if (rr->uselease && rr->state == regState_Registered)
-                       {
-                       if (rr->expire - m->timenow < 0)
-                               {
-                               debugf("refreshing record %##s", rr->resrec.name->c);
-                               rr->state = regState_Refresh;
-                               SendRecordRegistration(m, rr);
-                               }
-                       if (rr->expire - nextevent < 0) nextevent = rr->expire;
+                       if (nextevent - (rr->LastAPTime + rr->ThisAPInterval) > 0)
+                               nextevent = (rr->LastAPTime + rr->ThisAPInterval);
                        }
                }
        return nextevent;
@@ -4585,27 +4442,17 @@ mDNSlocal mDNSs32 CheckServiceRegistrations(mDNS *m)
                {
                ServiceRecordSet *srs = CurrentServiceRecordSet;
                CurrentServiceRecordSet = CurrentServiceRecordSet->uDNS_next;
-               if (srs->state == regState_Pending || srs->state == regState_DeregPending || srs->state == regState_DeregDeferred || srs->state == regState_Refresh || srs->state == regState_UpdatePending)
+               if (srs->state == regState_Pending || srs->state == regState_DeregPending  || srs->state == regState_DeregDeferred ||
+                       srs->state == regState_Refresh || srs->state == regState_UpdatePending || srs->state == regState_Registered)
                        {
-                       if (srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval - m->timenow < 0)
+                       if (srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval - m->timenow <= 0)
                                {
-                               if (srs->tcp) { srs->RR_SRV.LastAPTime = m->timenow; srs->RR_SRV.ThisAPInterval = 0x3FFFFFFF; }
-                               else if (srs->state == regState_DeregPending) { SendServiceDeregistration(m, srs); continue; }
+                               if (srs->tcp) { DisposeTCPConn(srs->tcp); srs->tcp = mDNSNULL; }
+                               if (srs->state == regState_DeregPending) SendServiceDeregistration(m, srs);
                                else SendServiceRegistration(m, srs);
                                }
-                       if (nextevent - srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval > 0)
-                               nextevent = srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval;
-                       }
-
-               if (srs->srs_uselease && srs->state == regState_Registered)
-                       {
-                       if (srs->expire - m->timenow < 0)
-                               {
-                               debugf("refreshing service %##s", srs->RR_SRV.resrec.name->c);
-                               srs->state = regState_Refresh;
-                               SendServiceRegistration(m, srs);
-                               }
-                       if (srs->expire - nextevent < 0) nextevent = srs->expire;
+                       if (nextevent - (srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval) > 0)
+                               nextevent = (srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval);
                        }
                }
        return nextevent;
@@ -4637,104 +4484,12 @@ mDNSexport void uDNS_Execute(mDNS *const m)
 #pragma mark - Startup, Shutdown, and Sleep
 #endif
 
-// DeregisterActive causes active LLQs to be removed from the server, e.g. before sleep. Pass false
-// following a location change, as the server will reject deletions from a source address different
-// from the address on which the LLQ was created.
-
-mDNSlocal void SuspendLLQs(mDNS *m, mDNSBool DeregisterActive)
-       {
-       DNSQuestion *q;
-
-       for (q = m->Questions; q; q = q->next)
-               {
-               if (q->LongLived)
-                       {
-                       if (q->state == LLQ_GetZoneInfo)
-                               {
-                               debugf("Marking %##s suspend-deferred", q->qname.c);
-                               q->state = LLQ_SuspendDeferred; // suspend once we're done getting zone info
-                               }
-                       else if (q->state < LLQ_Suspended)
-                               {
-                               if (DeregisterActive && (q->state == LLQ_Established || q->state == LLQ_Refresh))
-                                       {
-                                       debugf("Deleting LLQ %##s", q->qname.c);
-                                       sendLLQRefresh(m, q, 0);
-                                       }
-                               debugf("Marking %##s suspended", q->qname.c);
-                               q->state = LLQ_Suspended;
-
-                               if (q->tcp) { DisposeTCPConn(q->tcp); q->tcp = mDNSNULL; }
-
-                               q->id = zeroOpaque64;
-                               }
-                       else if (q->state == LLQ_Poll)
-                               {
-                               debugf("Marking %##s suspended-poll", q->qname.c);
-                               q->state = LLQ_SuspendedPoll;
-                               }
-
-                       RemoveLLQNatMappings(m, q); // may not need nat mapping if we restart with new route
-                       }
-               }
-       CheckForUnreferencedLLQMapping(m);
-       }
-
-mDNSlocal void RestartQueries(mDNS *m)
-       {
-       if (m->CurrentQuestion)
-               LogMsg("RestartQueries: ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
-       m->CurrentQuestion = m->Questions;
-       while (m->CurrentQuestion)
-               {
-               DNSQuestion *q = m->CurrentQuestion;
-               m->CurrentQuestion = m->CurrentQuestion->next;
-
-               if (!mDNSOpaque16IsZero(q->TargetQID) && !q->DuplicateOf)
-                       {
-                       if (q->LongLived)
-                               {
-                               if (q->state == LLQ_Suspended || q->state == LLQ_NatMapWaitUDP)
-                                       {
-                                       q->ntries = -1;
-                                       startLLQHandshake(m, q);
-                                       }
-                               else if (q->state == LLQ_SuspendDeferred)
-                                       q->state = LLQ_GetZoneInfo; // we never finished getting zone data - proceed as usual
-                               else if (q->state == LLQ_SuspendedPoll)
-                                       {
-                                       // if we were polling, we may have had bad zone data due to firewall, etc. - refetch
-                                       q->ntries = 0;
-                                       q->state = LLQ_GetZoneInfo;
-                                       if (q->nta) CancelGetZoneData(m, q->nta); // Make sure we cancel old one before we start a new one
-                                       q->nta = StartGetZoneData(m, &q->qname, ZoneServiceLLQ, startLLQHandshakeCallback, q);
-                                       }
-                               }
-                       else
-                               {
-                               q->LastQTime = m->timenow;
-                               q->ThisQInterval = INIT_UCAST_POLL_INTERVAL; // trigger poll in 3 seconds (to reduce packet rate when restarts come in rapid succession)
-                               SetNextQueryTime(m, q);
-                               }
-                       }
-               }
-       m->CurrentQuestion = mDNSNULL;
-       }
-
-mDNSexport void mDNS_UpdateLLQs(mDNS *m)
-       {
-       mDNS_Lock(m);
-       SuspendLLQs(m, mDNStrue);
-       RestartQueries(m);
-       mDNS_Unlock(m);
-       }
-
 // simplest sleep logic - rather than having sleep states that must be dealt with explicitly in all parts of
 // the code, we simply send a deregistration, and put the service in Refresh state, with a timeout far enough
 // in the future that we'll sleep (or the sleep will be cancelled) before it is retransmitted. Then to wake,
 // we just move up the timers.
 
-mDNSlocal void SleepRecordRegistrations(mDNS *m)
+mDNSexport void SleepRecordRegistrations(mDNS *m)
        {
        AuthRecord *rr = m->ResourceRecords;
 
@@ -4743,16 +4498,7 @@ mDNSlocal void SleepRecordRegistrations(mDNS *m)
                if (rr->state == regState_Registered ||
                        rr->state == regState_Refresh)
                        {
-                       mDNSu8 *ptr = m->omsg.data, *end = (mDNSu8 *)&m->omsg + sizeof(DNSMessage);
-                       InitializeDNSMessage(&m->omsg.h, mDNS_NewMessageID(m), UpdateReqFlags);
-
-                       // construct deletion update
-                       ptr = putZone(&m->omsg, ptr, end, &rr->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass));
-                       if (!ptr) { LogMsg("Error: SleepRecordRegistrations - could not put zone"); return; }
-                       ptr = putDeletionRecord(&m->omsg, ptr, &rr->resrec);
-                       if (!ptr) { LogMsg("Error: SleepRecordRegistrations - could not put deletion record"); return; }
-
-                       mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, &rr->UpdateServer, rr->UpdatePort, mDNSNULL, GetAuthInfoForName_internal(m, rr->resrec.name));
+                       SendRecordDeregistration(m, rr);
                        rr->state = regState_Refresh;
                        rr->LastAPTime = m->timenow;
                        rr->ThisAPInterval = 300 * mDNSPlatformOneSecond;
@@ -4777,7 +4523,7 @@ mDNSlocal void WakeRecordRegistrations(mDNS *m)
                }
        }
 
-mDNSlocal void SleepServiceRegistrations(mDNS *m)
+mDNSexport void SleepServiceRegistrations(mDNS *m)
        {
        ServiceRecordSet *srs = m->ServiceRegistrations;
        while (srs)
@@ -5031,16 +4777,8 @@ mDNSexport mStatus uDNS_RegisterSearchDomains(mDNS *const m)
 // 6) client calls to enumerate domains now go over LocalOnly interface
 //    (!!!KRS may add outgoing interface in addition)
 
-mDNSexport void uDNS_Sleep(mDNS *const m)
-       {
-       SuspendLLQs(m, mDNStrue);
-       SleepServiceRegistrations(m);
-       SleepRecordRegistrations(m);
-       }
-
 mDNSexport void uDNS_Wake(mDNS *const m)
        {
-       RestartQueries(m);
        WakeServiceRegistrations(m);
        WakeRecordRegistrations(m);
        }
index bf7918d36729069ff470817a84e84cd84d6dadce..c61e05c444cd9083d44d4baf6ce217d6f7920fe0 100755 (executable)
     Change History (most recent first):
 
 $Log: uDNS.h,v $
+Revision 1.88  2007/10/25 20:06:13  cheshire
+Don't try to do SOA queries using private DNS (TLS over TCP) queries
+
+Revision 1.87  2007/10/24 22:40:06  cheshire
+Renamed: RecordRegistrationCallback          -> RecordRegistrationGotZoneData
+Renamed: ServiceRegistrationZoneDataComplete -> ServiceRegistrationGotZoneData
+
+Revision 1.86  2007/10/18 23:06:42  cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+Additional fixes and refinements
+
+Revision 1.85  2007/10/18 20:23:17  cheshire
+Moved SuspendLLQs into mDNS.c, since it's only called from one place
+
+Revision 1.84  2007/10/17 22:49:54  cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+
+Revision 1.83  2007/10/17 22:37:23  cheshire
+<rdar://problem/5536979> BTMM: Need to create NAT port mapping for receiving LLQ events
+
+Revision 1.82  2007/10/17 21:53:51  cheshire
+Improved debugging messages; renamed startLLQHandshakeCallback to LLQGotZoneData
+
+Revision 1.81  2007/10/16 21:16:50  cheshire
+Get rid of unused uDNS_Sleep() routine
+
+Revision 1.80  2007/10/16 20:59:41  cheshire
+Export SuspendLLQs/SleepServiceRegistrations/SleepRecordRegistrations so they're callable from other files
+
 Revision 1.79  2007/09/20 01:13:19  cheshire
 Export CacheGroupForName so it's callable from other files
 
@@ -200,13 +229,15 @@ Revision 1.33  2006/07/05 22:53:28  cheshire
 
 // Entry points into unicast-specific routines
 
-extern void startLLQHandshakeCallback(mDNS *const m, mStatus err, const ZoneData *zoneInfo);
+extern void LLQGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo);
+extern void startLLQHandshake(mDNS *m, DNSQuestion *q);
+extern void sendLLQRefresh(mDNS *m, DNSQuestion *q, mDNSu32 lease);
 
-extern void    uDNS_StopLongLivedQuery(mDNS *const m, DNSQuestion *const question);
-
-extern void uDNS_Sleep(mDNS *const m);
 extern void uDNS_Wake(mDNS *const m);
 
+extern void SleepServiceRegistrations(mDNS *m);
+extern void SleepRecordRegistrations(mDNS *m);
+
 // uDNS_UpdateRecord
 // following fields must be set, and the update validated, upon entry.
 // rr->NewRData
@@ -228,11 +259,10 @@ extern mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const questi
 extern mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const question);
 extern mStatus mDNS_StartNATOperation_internal(mDNS *const m, NATTraversalInfo *traversal);
 
-extern void RecordRegistrationCallback(mDNS *const m, mStatus err, const ZoneData *zoneData);
-extern void GetZoneData_QuestionCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord);
+extern void RecordRegistrationGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneData);
 extern mStatus uDNS_DeregisterRecord(mDNS *const m, AuthRecord *const rr);
 
-extern void ServiceRegistrationZoneDataComplete(mDNS *const m, mStatus err, const ZoneData *result);
+extern void ServiceRegistrationGotZoneData(mDNS *const m, mStatus err, const ZoneData *result);
 extern const domainname *GetServiceTarget(mDNS *m, ServiceRecordSet *srs);
 extern mStatus uDNS_DeregisterService(mDNS *const m, ServiceRecordSet *srs);
 
@@ -251,13 +281,14 @@ extern mStatus         uDNS_RegisterSearchDomains(mDNS *const m);
 typedef enum
        {
        uDNS_LLQ_Not = 0,       // Normal uDNS answer: Flush any stale records from cache, and respect record TTL
-       uDNS_LLQ_Poll,          // LLQ Poll: Flush any stale records from cache, but assume TTL is 2 x poll interval
-       uDNS_LLQ_Setup,         // LLQ Initial answer packet: Flush any stale records from cache; assume TTL is 2 x LLQ refresh interval
+       uDNS_LLQ_Ignore,        // LLQ initial challenge packet: ignore -- has no useful records for us
+       uDNS_LLQ_Entire,        // LLQ initial set of answers: Flush any stale records from cache, but assume TTL is 2 x LLQ refresh interval
        uDNS_LLQ_Events         // LLQ event packet: don't flush cache; assume TTL is 2 x LLQ refresh interval
        } uDNS_LLQType;
 
 extern uDNS_LLQType    uDNS_recvLLQResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport);
 extern DomainAuthInfo *GetAuthInfoForName_internal(mDNS *m, const domainname *const name);
+extern DomainAuthInfo *GetAuthInfoForQuestion(mDNS *m, const DNSQuestion *const q);
 extern void DisposeTCPConn(struct tcpInfo_t *tcp);
 
 // NAT traversal
index 09db9ca9c53685bc58942fa4c5addad93a5b9514..2be3dd4aff65712d037fa7cc66347571446203de 100644 (file)
     Change History (most recent first):
 
 $Log: LegacyNATTraversal.c,v $
+Revision 1.44  2007/11/02 20:45:40  cheshire
+Don't log "connection failed" in customer builds
+
+Revision 1.43  2007/10/18 20:09:47  cheshire
+<rdar://problem/5545930> BTMM: Back to My Mac not working with D-Link DGL-4100 NAT gateway
+
+Revision 1.42  2007/10/16 17:37:18  cheshire
+<rdar://problem/3557903> Performance: Core code will not work on platforms with small stacks
+Cut SendSOAPMsgControlAction stack from 2144 to 96 bytes
+
+Revision 1.41  2007/10/15 23:02:00  cheshire
+Off-by-one error: Incorrect trailing zero byte on the end of the SSDP Discovery message
+
 Revision 1.40  2007/09/20 21:41:49  cheshire
 <rdar://problem/5495568> Legacy NAT Traversal - unmap request failed with error -65549
 
@@ -283,28 +296,25 @@ mDNSlocal void handleLNTGetExternalAddressResponse(tcpLNTInfo *tcpInfo)
        mDNS       *m = tcpInfo->m;
        mDNSu16     err = NATErr_None;
        mDNSv4Addr  ExtAddr;
-       char       *addrPtr;
        char       *ptr = (char *)tcpInfo->Reply;
        char       *end = (char *)tcpInfo->Reply + tcpInfo->nread;
+       char       *addrend;
+       static char tagname[20] = "NewExternalIPAddress";               // Array NOT including a terminating nul
 
 //     LogOperation("handleLNTGetExternalAddressResponse: %s", ptr);
 
-       while (ptr && ptr < end)        // Find "NewExternalIPAddress"
-               {
-               if (*ptr == 'N' && (strncasecmp(ptr, "NewExternalIPAddress", 20) == 0)) break;  // find the first 'N'; is this NewExternalIPAddress? if not, keep looking
-               ptr++;
-               }
-       if (ptr == mDNSNULL || ptr >= end) return;      // bad or incomplete response
-       ptr+=21;                                                                        // skip over "NewExternalIPAddress>"
-       if (ptr >= end) { LogOperation("handleLNTGetExternalAddressResponse: past end of buffer!"); return; }
-
-       // find the end of the address and terminate the string so inet_pton() can convert it
-       for (addrPtr = ptr; addrPtr && addrPtr < end; addrPtr++) if (*addrPtr == '<') break;    // first find the next '<' and count the chars
-       if (addrPtr == mDNSNULL || addrPtr >= end) { LogOperation("handleLNTGetExternalAddressResponse: didn't find SOAP URL string"); return; }
-       *addrPtr = '\0';
+       while (ptr < end && strncasecmp(ptr, tagname, sizeof(tagname))) ptr++;
+       ptr += sizeof(tagname);                                         // Skip over "NewExternalIPAddress"
+       while (ptr < end && *ptr != '>') ptr++;
+       ptr += 1;                                                                       // Skip over ">"
+       // Find the end of the address and terminate the string so inet_pton() can convert it
+       addrend = ptr;
+       while (addrend < end && (mdnsIsDigit(*addrend) || *addrend == '.')) addrend++;
+       if (addrend >= end) return;
+       *addrend = 0;
 
        if (inet_pton(AF_INET, ptr, &ExtAddr) <= 0)
-               { LogMsg("handleLNTGetExternalAddressResponse: Router returned bad address"); err = NATErr_NetFail; }
+               { LogMsg("handleLNTGetExternalAddressResponse: Router returned bad address %s", ptr); err = NATErr_NetFail; }
        if (!err) LogOperation("handleLNTGetExternalAddressResponse: External IP address is %.4a", &ExtAddr);
 
        natTraversalHandleAddressReply(m, err, ExtAddr);
@@ -319,7 +329,7 @@ mDNSlocal void handleLNTPortMappingResponse(tcpLNTInfo *tcpInfo)
        NATTraversalInfo *natInfo;
 
        for (natInfo = m->NATTraversals; natInfo; natInfo=natInfo->next) { if (natInfo == tcpInfo->parentNATInfo) break; }
-       
+
        if (!natInfo) { LogOperation("handleLNTPortMappingResponse: can't find matching tcpInfo in NATTraversals!"); return; }
 
        // start from the beginning of the HTTP header; find "200 OK" status message; if the first characters after the
@@ -465,7 +475,8 @@ mDNSlocal mStatus MakeTCPConnection(mDNS *const m, tcpLNTInfo *info, const mDNSA
                }
        else
                {
-               LogMsg("LNT MakeTCPConnection: connection failed");
+               // Don't need to log this in customer builds -- it happens quite often during sleep, wake, configuration changes, etc.
+               LogOperation("LNT MakeTCPConnection: connection failed");
                mDNSPlatformTCPCloseConnection(info->sock);     // Dispose the socket we created with mDNSPlatformTCPSocket() above
                info->sock = mDNSNULL;
                mDNSPlatformMemFree(info->Reply);
@@ -521,16 +532,16 @@ mDNSlocal mStatus SendSOAPMsgControlAction(mDNS *m, tcpLNTInfo *info, char *Acti
                "</SOAP-ENV:Envelope>\r\n";
 
        mStatus err;
-       char    body[2048];             // Typically requires 1110-1122 bytes, so 2048 allows a generous safety margin
+       char   *body = (char *)&m->omsg;                        // Typically requires 1110-1122 bytes; m->omsg is 8952 bytes, which is plenty
        int     bodyLen;
 
        if (m->UPnPSOAPURL == mDNSNULL || m->UPnPSOAPAddressString == mDNSNULL) // if no SOAP URL or address exists get out here
                { LogOperation("SendSOAPMsgControlAction: no SOAP URL or address string"); return mStatus_Invalid; }
 
        // Create body
-       bodyLen  = mDNS_snprintf   (body,           sizeof(body),           body1,   Action);
-       bodyLen += AddSOAPArguments(body + bodyLen, sizeof(body) - bodyLen, numArgs, Arguments);
-       bodyLen += mDNS_snprintf   (body + bodyLen, sizeof(body) - bodyLen, body2,   Action);
+       bodyLen  = mDNS_snprintf   (body,           sizeof(m->omsg),           body1,   Action);
+       bodyLen += AddSOAPArguments(body + bodyLen, sizeof(m->omsg) - bodyLen, numArgs, Arguments);
+       bodyLen += mDNS_snprintf   (body + bodyLen, sizeof(m->omsg) - bodyLen, body2,   Action);
 
        // Create info->Request; the header needs to contain the bodyLen in the "Content-Length" field
        if (!info->Request) info->Request = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE);
@@ -809,10 +820,10 @@ mDNSexport void LNT_SendDiscoveryMsg(mDNS *m)
                "Man:\"ssdp:discover\"\r\n"
                "MX:3\r\n\r\n";
 
-       LogOperation("LNT_SendDiscoveryMsg %.4a %.4a", &m->Router.ip.v4, &m->ExternalAddress);
+       LogOperation("LNT_SendDiscoveryMsg Router %.4a Current External Address %.4a", &m->Router.ip.v4, &m->ExternalAddress);
 
        if (!mDNSIPv4AddressIsZero(m->Router.ip.v4) && mDNSIPv4AddressIsZero(m->ExternalAddress))
-               mDNSPlatformSendUDP(m, msg, msg + sizeof(msg), 0, &m->Router, SSDPPort);
+               mDNSPlatformSendUDP(m, msg, msg + sizeof(msg) - 1, 0, &m->Router, SSDPPort);
        }
 
 #endif /* _LEGACY_NAT_TRAVERSAL_ */
index 714c43197589f80c0a2bb8c2536f7b5c9be9b825..e7b7b9d0232b7ae72cd38068321e8e56d5478870 100644 (file)
     Change History (most recent first):
 
 $Log: daemon.c,v $
+Revision 1.347  2007/11/02 22:00:13  cheshire
+<rdar://problem/5575583> BTMM: Work around keychain notification bug <rdar://problem/5124399>
+Need to hold the lock while calling SetDomainSecrets
+
+Revision 1.346  2007/11/02 20:18:13  cheshire
+<rdar://problem/5575583> BTMM: Work around keychain notification bug <rdar://problem/5124399>
+
+Revision 1.345  2007/10/17 18:41:21  cheshire
+For debugging, make SIGUSR1 simulate a KeychainChanged event as well as a NetworkChanged
+
 Revision 1.344  2007/09/29 01:06:17  mcguire
 <rdar://problem/5507862> 9A564: mDNSResponder crash in mDNS_Execute
 
@@ -2042,11 +2052,13 @@ mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void
        (void)size;             // Unused
        (void)info;             // Unused
        mach_msg_header_t *msg_header = (mach_msg_header_t *)msg;
-       KQueueLock(&mDNSStorage);
+       mDNS *const m = &mDNSStorage;
+
+       // We're running on the CFRunLoop (Mach port) thread, not the kqueue thread, so we need to grab the KQueueLock before proceeding
+       KQueueLock(m);
        switch(msg_header->msgh_id)
                {
                case SIGHUP:    {
-                                               mDNS *m = &mDNSStorage;
                                                mDNSu32 slot;
                                                CacheGroup *cg;
                                                CacheRecord *rr;
@@ -2057,11 +2069,18 @@ mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void
                case SIGTERM:   ExitCallback(msg_header->msgh_id); break;
                case SIGINFO:   INFOCallback(); break;
                case SIGUSR1:   LogMsg("SIGUSR1: Simulate Network Configuration Change Event");
-                                               mDNSMacOSXNetworkChanged(&mDNSStorage); break;
+                                               mDNSMacOSXNetworkChanged(m);
+
+                                               // Simulate KeychainChanged
+                                               mDNS_Lock(m);
+                                               SetDomainSecrets(m);
+                                               mDNS_Unlock(m);
+
+                                               break;
                case SIGUSR2:   SigLogLevel(); break;
                default: LogMsg("SignalCallback: Unknown signal %d", msg_header->msgh_id); break;
                }
-       KQueueUnlock(&mDNSStorage, "Unix Signal");
+       KQueueUnlock(m, "Unix Signal");
        }
 
 // On 10.2 the MachServerName is DNSServiceDiscoveryServer
@@ -2131,6 +2150,20 @@ mDNSlocal mDNSs32 mDNSDaemonIdle(mDNS *const m)
        // we then systematically lose our own looped-back packets.
        if (m->p->NetworkChanged && now - m->p->NetworkChanged >= 0) mDNSMacOSXNetworkChanged(m);
 
+       // KeyChain frequently fails to notify clients of change events. To work around this
+       // we set a timer and periodically poll to detect if any changes have occurred.
+       // Without this Back To My Mac just does't work for a large number of users.
+       // See <rdar://problem/5124399> Not getting Keychain Changed events when enabling BTMM
+       if (m->p->KeyChainBugTimer && now - m->p->KeyChainBugTimer >= 0)
+               {
+               m->p->KeyChainBugTimer = NonZeroTime(now + m->p->KeyChainBugInterval);
+               m->p->KeyChainBugInterval *= 2;
+               if (m->p->KeyChainBugInterval > 16 * mDNSPlatformOneSecond) m->p->KeyChainBugTimer = 0;
+               mDNS_Lock(m);
+               SetDomainSecrets(m);
+               mDNS_Unlock(m);
+               }
+
        // 2. Call mDNS_Execute() to let mDNSCore do what it needs to do
        mDNSs32 nextevent = mDNS_Execute(m);
 
@@ -2138,6 +2171,10 @@ mDNSlocal mDNSs32 mDNSDaemonIdle(mDNS *const m)
                if (nextevent - m->p->NetworkChanged > 0)
                        nextevent = m->p->NetworkChanged;
 
+       if (m->p->KeyChainBugTimer)
+               if (nextevent - m->p->KeyChainBugTimer > 0)
+                       nextevent = m->p->KeyChainBugTimer;
+
        // 3. Deliver any waiting browse messages to clients
        DNSServiceBrowser *b = DNSServiceBrowserList;
 
index bf316b5859c60d4735f817cf0e30dd36497d9123..5cba5efa009f222f02f9b8b22dd6f20732baca68 100644 (file)
     Change History (most recent first):
 
 $Log: mDNSMacOSX.c,v $
+Revision 1.508  2007/11/02 21:59:37  cheshire
+Added comment about locking
+
+Revision 1.507  2007/11/02 20:18:13  cheshire
+<rdar://problem/5575583> BTMM: Work around keychain notification bug <rdar://problem/5124399>
+
+Revision 1.506  2007/10/30 20:46:45  cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+
+Revision 1.505  2007/10/29 23:55:10  cheshire
+<rdar://problem/5526791> BTMM: Changing Local Hostname doesn't update Back to My Mac registered records
+Don't need to manually fake another AutoTunnelNATCallback if it has not yet received its first callback
+(and indeed should not, since the result fields will not yet be set up correctly in this case)
+
+Revision 1.504  2007/10/26 00:50:37  cheshire
+<rdar://problem/5526791> BTMM: Changing Local Hostname doesn't update Back to My Mac registered records
+
+Revision 1.503  2007/10/25 23:11:42  cheshire
+Ignore IPv6 ULA addresses configured on lo0 loopback interface
+
+Revision 1.502  2007/10/22 20:07:07  cheshire
+Moved mDNSPlatformSourceAddrForDest from mDNSMacOSX.c to PlatformCommon.c so
+Posix build can share the code (better than just pasting it into mDNSPosix.c)
+
+Revision 1.501  2007/10/22 19:40:30  cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+Made subroutine mDNSPlatformSourceAddrForDest(mDNSAddr *const src, const mDNSAddr *const dst)
+
+Revision 1.500  2007/10/17 22:49:55  cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+
+Revision 1.499  2007/10/17 19:47:54  cheshire
+Improved debugging messages
+
+Revision 1.498  2007/10/17 18:42:06  cheshire
+Export SetDomainSecrets so its callable from other files
+
+Revision 1.497  2007/10/16 17:03:07  cheshire
+<rdar://problem/3557903> Performance: Core code will not work on platforms with small stacks
+Cut SetDomainSecrets stack from 3792 to 1760 bytes
+
 Revision 1.496  2007/10/04 20:33:05  mcguire
 <rdar://problem/5518845> BTMM: Racoon configuration removed when network changes
 
@@ -936,6 +977,9 @@ mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const ms
        else
                {
                LogMsg("mDNSPlatformSendUDP: dst is not an IPv4 or IPv6 address!");
+#if ForceAlerts
+               *(long*)0 = 0;
+#endif
                return mStatus_BadParamErr;
                }
 
@@ -2055,33 +2099,55 @@ mDNSlocal mDNSBool TunnelClients(mDNS *const m)
 
 mDNSlocal void RegisterAutoTunnelRecords(mDNS *m, DomainAuthInfo *info)
        {
-       if (!info->AutoTunnelNAT.Result && !mDNSIPPortIsZero(info->AutoTunnelNAT.ExternalPort) && AutoTunnelUnregistered(info))
+       if (info->AutoTunnelNAT.clientContext && !info->AutoTunnelNAT.Result && !mDNSIPPortIsZero(info->AutoTunnelNAT.ExternalPort) && AutoTunnelUnregistered(info))
                {
-               LogOperation("RegisterAutoTunnelRecords %##s", info->AutoTunnelService.namestorage.c);
                mStatus err;
-               info->AutoTunnelService.resrec.rdata->u.srv.port = info->AutoTunnelNAT.ExternalPort;
-               info->AutoTunnelService.resrec.RecordType = kDNSRecordTypeKnownUnique;
-               err = mDNS_Register(m, &info->AutoTunnelService);
-               if (err) LogMsg("RegisterAutoTunnelRecords error %d registering AutoTunnelService %##s", err, info->AutoTunnelService.namestorage.c);
-               
-               info->AutoTunnelTarget.resrec.RecordType = kDNSRecordTypeKnownUnique;
-               mDNS_Lock(m);
-               mDNS_AddDynDNSHostName(m, &info->AutoTunnelTarget.namestorage, mDNSNULL, info);
-               mDNS_Unlock(m);
+               LogOperation("RegisterAutoTunnelRecords %##s (%#s)", info->domain.c, m->hostlabel.c);
 
-               if (info->AutoTunnelHostRecord.namestorage.c[0] == 0)
-                       {
-                       AppendDomainLabel(&info->AutoTunnelHostRecord.namestorage, &m->hostlabel);
-                       AppendDomainName (&info->AutoTunnelHostRecord.namestorage, &info->domain);
-                       }
+               // 1. Set up our address record for the internal tunnel address
+               // (User-visible user-friendly host name, used as target in AutoTunnel SRV records)
+               info->AutoTunnelHostRecord.namestorage.c[0] = 0;
+               AppendDomainLabel(&info->AutoTunnelHostRecord.namestorage, &m->hostlabel);
+               AppendDomainName (&info->AutoTunnelHostRecord.namestorage, &info->domain);
+               info->AutoTunnelHostRecord.resrec.rdata->u.ipv6 = m->AutoTunnelHostAddr;
                info->AutoTunnelHostRecord.resrec.RecordType = kDNSRecordTypeKnownUnique;
                err = mDNS_Register(m, &info->AutoTunnelHostRecord);
                if (err) LogMsg("RegisterAutoTunnelRecords error %d registering AutoTunnelHostRecord %##s", err, info->AutoTunnelHostRecord.namestorage.c);     
 
+               // 2. Set up device info record
+               ConstructServiceName(&info->AutoTunnelDeviceInfo.namestorage, &m->nicelabel, &DeviceInfoName, &info->domain);
+               mDNSu8 len = m->HIHardware.c[0] < 255 - 6 ? m->HIHardware.c[0] : 255 - 6;
+               mDNSPlatformMemCopy(info->AutoTunnelDeviceInfo.resrec.rdata->u.data + 1, "model=", 6);
+               mDNSPlatformMemCopy(info->AutoTunnelDeviceInfo.resrec.rdata->u.data + 7, m->HIHardware.c + 1, len);
+               info->AutoTunnelDeviceInfo.resrec.rdata->u.data[0] = 6 + len;   // "model=" plus the device string
+               info->AutoTunnelDeviceInfo.resrec.rdlength         = 7 + len;   // One extra for the length byte at the start of the string
                info->AutoTunnelDeviceInfo.resrec.RecordType = kDNSRecordTypeKnownUnique;
                err = mDNS_Register(m, &info->AutoTunnelDeviceInfo);
                if (err) LogMsg("RegisterAutoTunnelRecords error %d registering AutoTunnelDeviceInfo %##s", err, info->AutoTunnelDeviceInfo.namestorage.c);
 
+               // 3. Set up our address record for the external tunnel address
+               // (Constructed name, not generally user-visible, used as target in IKE tunnel's SRV record)
+               info->AutoTunnelTarget.namestorage.c[0] = 0;
+               AppendDomainLabel(&info->AutoTunnelTarget.namestorage, &m->AutoTunnelLabel);
+               AppendDomainName (&info->AutoTunnelTarget.namestorage, &info->domain);
+               info->AutoTunnelTarget.resrec.RecordType = kDNSRecordTypeKnownUnique;
+
+               mDNS_Lock(m);
+               mDNS_AddDynDNSHostName(m, &info->AutoTunnelTarget.namestorage, mDNSNULL, info);
+               mDNS_Unlock(m);
+
+               // 4. Set up IKE tunnel's SRV record: "AutoTunnelHostRecord SRV 0 0 port AutoTunnelTarget"
+               AssignDomainName (&info->AutoTunnelService.namestorage, (const domainname*) "\x0B" "_autotunnel" "\x04" "_udp");
+               AppendDomainLabel(&info->AutoTunnelService.namestorage, &m->hostlabel);
+               AppendDomainName (&info->AutoTunnelService.namestorage, &info->domain);
+               info->AutoTunnelService.resrec.rdata->u.srv.priority = 0;
+               info->AutoTunnelService.resrec.rdata->u.srv.weight   = 0;
+               info->AutoTunnelService.resrec.rdata->u.srv.port     = info->AutoTunnelNAT.ExternalPort;
+               AssignDomainName(&info->AutoTunnelService.resrec.rdata->u.srv.target, &info->AutoTunnelTarget.namestorage);
+               info->AutoTunnelService.resrec.RecordType = kDNSRecordTypeKnownUnique;
+               err = mDNS_Register(m, &info->AutoTunnelService);
+               if (err) LogMsg("RegisterAutoTunnelRecords error %d registering AutoTunnelService %##s", err, info->AutoTunnelService.namestorage.c);
+
                LogMsg("AutoTunnel server listening for connections on %##s[%.4a]:%d:%##s[%.16a]",
                        info->AutoTunnelTarget.namestorage.c,     &m->AdvertisedV4.ip.v4, mDNSVal16(info->AutoTunnelNAT.IntPort),
                        info->AutoTunnelHostRecord.namestorage.c, &m->AutoTunnelHostAddr);
@@ -2090,7 +2156,7 @@ mDNSlocal void RegisterAutoTunnelRecords(mDNS *m, DomainAuthInfo *info)
 
 mDNSlocal void DeregisterAutoTunnelRecords(mDNS *m, DomainAuthInfo *info)
        {
-       LogOperation("DeregisterAutoTunnelRecords %##s", info->AutoTunnelService.namestorage.c);
+       LogOperation("DeregisterAutoTunnelRecords %##s", info->domain.c);
        if (info->AutoTunnelService.resrec.RecordType > kDNSRecordTypeDeregistering)
                {
                mStatus err = mDNS_Deregister(m, &info->AutoTunnelService);
@@ -2113,7 +2179,6 @@ mDNSlocal void DeregisterAutoTunnelRecords(mDNS *m, DomainAuthInfo *info)
                        info->AutoTunnelHostRecord.resrec.RecordType = kDNSRecordTypeUnregistered;
                        LogMsg("DeregisterAutoTunnelRecords error %d deregistering AutoTunnelHostRecord %##s", err, info->AutoTunnelHostRecord.namestorage.c);
                        }
-               info->AutoTunnelHostRecord.namestorage.c[0] = 0;
                }
 
        if (info->AutoTunnelDeviceInfo.resrec.RecordType > kDNSRecordTypeDeregistering)
@@ -2140,7 +2205,8 @@ mDNSlocal void AutoTunnelRecordCallback(mDNS *const m, AuthRecord *const rr, mSt
 mDNSlocal void AutoTunnelNATCallback(mDNS *m, NATTraversalInfo *n)
        {
        DomainAuthInfo *info = (DomainAuthInfo *)n->clientContext;
-       LogOperation("AutoTunnelNATCallback Result %d %.4a Internal %d External %d %##s", n->Result, &n->ExternalAddress, mDNSVal16(n->IntPort), mDNSVal16(n->ExternalPort), info->AutoTunnelService.namestorage.c);
+       LogOperation("AutoTunnelNATCallback Result %d %.4a Internal %d External %d %#s.%##s",
+               n->Result, &n->ExternalAddress, mDNSVal16(n->IntPort), mDNSVal16(n->ExternalPort), m->hostlabel.c, info->domain.c);
 
        m->NextSRVUpdate = m->timenow;
        DeregisterAutoTunnelRecords(m,info);
@@ -2159,6 +2225,17 @@ mDNSlocal void AutoTunnelNATCallback(mDNS *m, NATTraversalInfo *n)
                }
        }
 
+mDNSlocal void AbortDeregistration(mDNS *const m, AuthRecord *rr)
+       {
+       if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
+               {
+               LogOperation("Aborting deregistration of %s", ARDisplayString(m, rr));
+               CompleteDeregistration(m, rr);
+               }
+       else if (rr->resrec.RecordType != kDNSRecordTypeUnregistered)
+               LogMsg("AbortDeregistration ERROR RecordType %02X for %s", ARDisplayString(m, rr));
+       }
+
 // Before SetupLocalAutoTunnelInterface_internal is called,
 // m->AutoTunnelHostAddr.b[0] must be non-zero, and there must be at least one TunnelClient or TunnelServer
 // Must be called with the lock held
@@ -2182,36 +2259,15 @@ mDNSexport void SetupLocalAutoTunnelInterface_internal(mDNS *const m)
                        {
                        if (info->AutoTunnel && !info->deltime && !info->AutoTunnelNAT.clientContext)
                                {
-                               // 1. Set up our address record for the internal tunnel address
-                               // (User-visible user-friendly host name, used as target in AutoTunnel SRV records)
+                               // If we just resurrected a DomainAuthInfo that is still deregistering, we need to abort the deregistration process before re-using the AuthRecord memory
+                               AbortDeregistration(m, &info->AutoTunnelHostRecord);
+                               AbortDeregistration(m, &info->AutoTunnelDeviceInfo);
+                               AbortDeregistration(m, &info->AutoTunnelService);
+
                                mDNS_SetupResourceRecord(&info->AutoTunnelHostRecord, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL, kDNSRecordTypeUnregistered, AutoTunnelRecordCallback, info);
-                               info->AutoTunnelHostRecord.namestorage.c[0] = 0;
-                               info->AutoTunnelHostRecord.resrec.rdata->u.ipv6 = m->AutoTunnelHostAddr;
-
-                               // 2. Set up device info record
-                               mDNSu8 len = m->HIHardware.c[0] < 255 - 6 ? m->HIHardware.c[0] : 255 - 6;
-                               mDNS_SetupResourceRecord(&info->AutoTunnelDeviceInfo, mDNSNULL, mDNSInterface_Any, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnregistered, AutoTunnelRecordCallback, info);
-                               ConstructServiceName(&info->AutoTunnelDeviceInfo.namestorage, &m->nicelabel, &DeviceInfoName, &info->domain);
-                               mDNSPlatformMemCopy(info->AutoTunnelDeviceInfo.resrec.rdata->u.data + 1, "model=", 6);
-                               mDNSPlatformMemCopy(info->AutoTunnelDeviceInfo.resrec.rdata->u.data + 7, m->HIHardware.c + 1, len);
-                               info->AutoTunnelDeviceInfo.resrec.rdata->u.data[0] = 6 + len;   // "model=" plus the device string
-                               info->AutoTunnelDeviceInfo.resrec.rdlength         = 7 + len;   // One extra for the length byte at the start of the string
-
-                               // 3. Set up our address record for the external tunnel address
-                               // (Constructed name, not generally user-visible, used as target in IKE tunnel's SRV record)
-                               mDNS_SetupResourceRecord(&info->AutoTunnelTarget, mDNSNULL, mDNSInterface_Any, kDNSType_A, kHostNameTTL, kDNSRecordTypeUnregistered, AutoTunnelRecordCallback, info);
-                               info->AutoTunnelTarget.namestorage.c[0] = 0;
-                               AppendDomainLabel(&info->AutoTunnelTarget.namestorage, &m->AutoTunnelLabel);
-                               AppendDomainName (&info->AutoTunnelTarget.namestorage, &info->domain);
-
-                               // 4. Set up IKE tunnel's SRV record: "AutoTunnelHostRecord SRV 0 0 port AutoTunnelTarget"
-                               mDNS_SetupResourceRecord(&info->AutoTunnelService, mDNSNULL, mDNSInterface_Any, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnregistered, AutoTunnelRecordCallback, info);
-                               AssignDomainName(&info->AutoTunnelService.namestorage, (const domainname*) "\x0B" "_autotunnel" "\x04" "_udp");
-                               AppendDomainLabel(&info->AutoTunnelService.namestorage, &m->hostlabel);
-                               AppendDomainName (&info->AutoTunnelService.namestorage, &info->domain);
-                               info->AutoTunnelService.resrec.rdata->u.srv.priority = 0;
-                               info->AutoTunnelService.resrec.rdata->u.srv.weight   = 0;
-                               AssignDomainName(&info->AutoTunnelService.resrec.rdata->u.srv.target, &info->AutoTunnelTarget.namestorage);
+                               mDNS_SetupResourceRecord(&info->AutoTunnelDeviceInfo, mDNSNULL, mDNSInterface_Any, kDNSType_TXT,  kStandardTTL, kDNSRecordTypeUnregistered, AutoTunnelRecordCallback, info);
+                               mDNS_SetupResourceRecord(&info->AutoTunnelTarget,     mDNSNULL, mDNSInterface_Any, kDNSType_A,    kHostNameTTL, kDNSRecordTypeUnregistered, AutoTunnelRecordCallback, info);
+                               mDNS_SetupResourceRecord(&info->AutoTunnelService,    mDNSNULL, mDNSInterface_Any, kDNSType_SRV,  kHostNameTTL, kDNSRecordTypeUnregistered, AutoTunnelRecordCallback, info);
 
                                // Try to get a NAT port mapping for the AutoTunnelService
                                info->AutoTunnelNAT.clientCallback   = AutoTunnelNATCallback;
@@ -2320,7 +2376,7 @@ mDNSexport void AutoTunnelCallback(mDNS *const m, DNSQuestion *question, const R
                mDNSAddr tmpDst = { mDNSAddrType_IPv4, {{{0}}} };
                tmpDst.ip.v4 = tun->rmt_outer;
                mDNSAddr tmpSrc = zeroAddr;
-               FindSourceAddrForIP(&tmpDst, &tmpSrc);
+               mDNSPlatformSourceAddrForDest(&tmpSrc, &tmpDst);
                if (tmpSrc.type == mDNSAddrType_IPv4) tun->loc_outer = tmpSrc.ip.v4;
                else tun->loc_outer = m->AdvertisedV4.ip.v4;
 
@@ -2470,9 +2526,9 @@ mDNSlocal mStatus UpdateInterfaceList(mDNS *const m, mDNSs32 utc)
                                        {
                                        int ifru_flags6 = 0;
 #ifndef NO_IPV6
+                                       struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
                                        if (ifa->ifa_addr->sa_family == AF_INET6 && InfoSocket >= 0)
                                                {
-                                               struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
                                                struct in6_ifreq ifr6;
                                                mDNSPlatformMemZero((char *)&ifr6, sizeof(ifr6));
                                                strlcpy(ifr6.ifr_name, ifa->ifa_name, sizeof(ifr6.ifr_name));
@@ -2485,8 +2541,12 @@ mDNSlocal mStatus UpdateInterfaceList(mDNS *const m, mDNSs32 utc)
                                        if (!(ifru_flags6 & (IN6_IFF_NOTREADY | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED | IN6_IFF_TEMPORARY)))
                                                {
                                                if (ifa->ifa_flags & IFF_LOOPBACK)
-                                                       if (ifa->ifa_addr->sa_family == AF_INET) v4Loopback = ifa;
-                                                       else                                     v6Loopback = ifa;
+                                                       {
+                                                       if (ifa->ifa_addr->sa_family == AF_INET)     v4Loopback = ifa;
+#ifndef NO_IPV6
+                                                       else if (sin6->sin6_addr.s6_addr[0] != 0xFD) v6Loopback = ifa;
+#endif
+                                                       }
                                                else
                                                        {
                                                        NetworkInterfaceInfoOSX *i = AddInterfaceToList(m, ifa, utc);
@@ -2577,6 +2637,8 @@ mDNSlocal mStatus UpdateInterfaceList(mDNS *const m, mDNSs32 utc)
                MakeDomainLabelFromLiteralString(&hostlabel, defaultname);
                }
 
+       mDNSBool namechange = mDNSfalse;
+
        // We use a case-sensitive comparison here because even though changing the capitalization
        // of the name alone is not significant to DNS, it's still a change from the user's point of view
        if (SameDomainLabelCS(m->p->usernicelabel.c, nicelabel.c))
@@ -2586,6 +2648,7 @@ mDNSlocal mStatus UpdateInterfaceList(mDNS *const m, mDNSs32 utc)
                if (m->p->usernicelabel.c[0])   // Don't show message first time through, when we first read name from prefs on boot
                        LogMsg("User updated Computer Name from %#s to %#s", m->p->usernicelabel.c, nicelabel.c);
                m->p->usernicelabel = m->nicelabel = nicelabel;
+               namechange = mDNStrue;
                }
 
        if (SameDomainLabelCS(m->p->userhostlabel.c, hostlabel.c))
@@ -2596,7 +2659,18 @@ mDNSlocal mStatus UpdateInterfaceList(mDNS *const m, mDNSs32 utc)
                        LogMsg("User updated Local Hostname from %#s to %#s", m->p->userhostlabel.c, hostlabel.c);
                m->p->userhostlabel = m->hostlabel = hostlabel;
                mDNS_SetFQDN(m);
+               namechange = mDNStrue;
+               }
+
+#if APPLE_OSX_mDNSResponder
+       if (namechange)         // If either name has changed, we need to tickle our AutoTunnel state machine to update its registered records
+               {
+               DomainAuthInfo *info;
+               for (info = m->AuthInfoList; info; info = info->next)
+                       if (info->AutoTunnelNAT.clientContext && !mDNSIPv4AddressIsOnes(info->AutoTunnelNAT.ExternalAddress))
+                               AutoTunnelNATCallback(m, &info->AutoTunnelNAT);
                }
+#endif
 
        return(mStatus_NoError);
        }
@@ -2835,7 +2909,7 @@ mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDN
                        }
                else
                        {
-                       LogOperation("mDNSPlatformSetDNSConfig: Registering %d resolvers", config->n_resolver);
+                       LogOperation("mDNSPlatformSetDNSConfig: config->n_resolver = %d", config->n_resolver);
                        if (setservers)
                                {
                                for (i = 0; i < config->n_resolver; i++)
@@ -3257,7 +3331,7 @@ mDNSexport void mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const
        }
 
 // MUST be called holding the lock -- this routine calls SetupLocalAutoTunnelInterface_internal()
-mDNSlocal void SetDomainSecrets(mDNS *m)
+mDNSexport void SetDomainSecrets(mDNS *m)
        {
 #ifdef NO_SECURITYFRAMEWORK
        (void)m;
@@ -3288,6 +3362,7 @@ mDNSlocal void SetDomainSecrets(mDNS *m)
        }
 #endif APPLE_OSX_mDNSResponder
 
+       // String Array used to write list of private domains to Dynamic Store
        CFMutableArrayRef sa = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
        if (!sa) { LogMsg("SetDomainSecrets: CFArrayCreateMutable failed"); return; }
        CFIndex i;
@@ -3311,35 +3386,35 @@ mDNSlocal void SetDomainSecrets(mDNS *m)
                                if (CFDataGetTypeID() != CFGetTypeID(CFArrayGetValueAtIndex(entry, j)))
                                        { LogMsg("SetDomainSecrets: malformed entry item"); continue; }
 
-                       // Validate that attributes are not too large
-                       char dstring[MAX_ESCAPED_DOMAIN_NAME];
-                       char keynamebuf[MAX_ESCAPED_DOMAIN_NAME];       // Max legal C-string name, including terminating NUL
                        // The names have already been vetted by the helper, but checking them again here helps humans and automated tools verify correctness
+
+                       // Get DNS domain this key is for
+                       char stringbuf[MAX_ESCAPED_DOMAIN_NAME];        // Max legal domainname as C-string, including terminating NUL
                        data = CFArrayGetValueAtIndex(entry, 0);
-                       if (CFDataGetLength(data) >= (int)sizeof(dstring))
+                       if (CFDataGetLength(data) >= (int)sizeof(stringbuf))
                                { LogMsg("SetDomainSecrets: Bad kSecServiceItemAttr length %d", CFDataGetLength(data)); continue; }
-                       CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (UInt8 *)dstring);
-                       dstring[CFDataGetLength(data)] = '\0';
-                       data = CFArrayGetValueAtIndex(entry, 1);
-                       if (CFDataGetLength(data) >= (int)sizeof(keynamebuf))
-                               { LogMsg("SetDomainSecrets: Bad kSecAccountItemAttr length %d", CFDataGetLength(data)); continue; }
-                       CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8 *)keynamebuf);
-                       keynamebuf[CFDataGetLength(data)] = '\0';
+                       CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (UInt8 *)stringbuf);
+                       stringbuf[CFDataGetLength(data)] = '\0';
 
                        domainname domain;
-                       if (!MakeDomainNameFromDNSNameString(&domain, dstring))     { LogMsg("SetDomainSecrets: bad key domain %s", dstring);  continue; }
+                       if (!MakeDomainNameFromDNSNameString(&domain, stringbuf)) { LogMsg("SetDomainSecrets: bad key domain %s", stringbuf); continue; }
+
+                       // Get key name
+                       data = CFArrayGetValueAtIndex(entry, 1);
+                       if (CFDataGetLength(data) >= (int)sizeof(stringbuf))
+                               { LogMsg("SetDomainSecrets: Bad kSecAccountItemAttr length %d", CFDataGetLength(data)); continue; }
+                       CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8 *)stringbuf);
+                       stringbuf[CFDataGetLength(data)] = '\0';
 
-                       // Get DNS key name
                        domainname keyname;
-                       if (!MakeDomainNameFromDNSNameString(&keyname, keynamebuf)) { LogMsg("SetDomainSecrets: bad key name %s", keynamebuf); continue; }
+                       if (!MakeDomainNameFromDNSNameString(&keyname, stringbuf)) { LogMsg("SetDomainSecrets: bad key name %s", stringbuf); continue; }
 
-                       // Get DNS key data
-                       char keystring[1024];
+                       // Get key data
                        data = CFArrayGetValueAtIndex(entry, 2);
-                       if (CFDataGetLength(data) >= (int)sizeof(keystring))
+                       if (CFDataGetLength(data) >= (int)sizeof(stringbuf))
                                { LogMsg("SetDomainSecrets: Shared secret too long: %d", CFDataGetLength(data)); continue; }
-                       CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (UInt8 *)keystring);
-                       keystring[CFDataGetLength(data)] = '\0';        // mDNS_SetSecretForDomain requires NULL-terminated C string for key
+                       CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (UInt8 *)stringbuf);
+                       stringbuf[CFDataGetLength(data)] = '\0';        // mDNS_SetSecretForDomain requires NULL-terminated C string for key
 
                        DomainAuthInfo *FoundInList;
                        for (FoundInList = m->AuthInfoList; FoundInList; FoundInList = FoundInList->next)
@@ -3356,24 +3431,24 @@ mDNSlocal void SetDomainSecrets(mDNS *m)
                                        LogOperation("SetDomainSecrets: tunnel to %##s no longer marked for deletion", client->dstname.c);
                                        client->markedForDeletion = mDNSfalse;
                                        // If the key has changed, reconfigure the tunnel
-                                       if (strncmp(keystring, client->b64keydata, sizeof(client->b64keydata)))
+                                       if (strncmp(stringbuf, client->b64keydata, sizeof(client->b64keydata)))
                                                {
                                                mDNSBool queryNotInProgress = client->q.ThisQInterval < 0;
                                                LogOperation("SetDomainSecrets: secret changed for tunnel %##s %s", client->dstname.c, queryNotInProgress ? "reconfiguring" : "query in progress");
                                                if (queryNotInProgress) AutoTunnelSetKeys(client, mDNSfalse);
-                                               mDNS_snprintf(client->b64keydata, sizeof(client->b64keydata), "%s", keystring);
+                                               mDNS_snprintf(client->b64keydata, sizeof(client->b64keydata), "%s", stringbuf);
                                                if (queryNotInProgress) AutoTunnelSetKeys(client, mDNStrue);
                                                }
                                        }
                                }
 
-                       mDNSBool keyChanged = FoundInList && FoundInList->AutoTunnel ? strncmp(keystring, FoundInList->b64keydata, sizeof(FoundInList->b64keydata)) : mDNSfalse;
+                       mDNSBool keyChanged = FoundInList && FoundInList->AutoTunnel ? strncmp(stringbuf, FoundInList->b64keydata, sizeof(FoundInList->b64keydata)) : mDNSfalse;
 
 #endif APPLE_OSX_mDNSResponder
 
                        // Uncomment the line below to view the keys as they're read out of the system keychain
                        // DO NOT SHIP CODE THIS WAY OR YOU'LL LEAK SECRET DATA INTO A PUBLICLY READABLE FILE!
-                       //LogOperation("SetDomainSecrets: %##s %##s %s", &domain.c, &keyname.c, keystring);
+                       //LogOperation("SetDomainSecrets: %##s %##s %s", &domain.c, &keyname.c, stringbuf);
 
                        // If didn't find desired domain in the list, make a new entry
                        ptr = FoundInList;
@@ -3385,7 +3460,7 @@ mDNSlocal void SetDomainSecrets(mDNS *m)
                                }
 
                        LogOperation("SetDomainSecrets: %d of %d %##s", i, ArrayCount, &domain);
-                       if (mDNS_SetSecretForDomain(m, ptr, &domain, &keyname, keystring, IsTunnelModeDomain(&domain)) == mStatus_BadParamErr)
+                       if (mDNS_SetSecretForDomain(m, ptr, &domain, &keyname, stringbuf, IsTunnelModeDomain(&domain)) == mStatus_BadParamErr)
                                {
                                if (!FoundInList) mDNSPlatformMemFree(ptr);             // If we made a new DomainAuthInfo here, and it turned out bad, dispose it immediately
                                continue;
@@ -3395,11 +3470,12 @@ mDNSlocal void SetDomainSecrets(mDNS *m)
                        if (keyChanged && AnonymousRacoonConfig)
                                {
                                LogOperation("SetDomainSecrets: secret changed for %##s", &domain);
-                               (void)mDNSConfigureServer(kmDNSUp, keystring);
+                               (void)mDNSConfigureServer(kmDNSUp, stringbuf);
                                }
 #endif APPLE_OSX_mDNSResponder
 
-                       CFStringRef cfs = CFStringCreateWithCString(NULL, dstring, kCFStringEncodingUTF8);
+                       ConvertDomainNameToCString(&domain, stringbuf);
+                       CFStringRef cfs = CFStringCreateWithCString(NULL, stringbuf, kCFStringEncodingUTF8);
                        if (cfs) { CFArrayAppendValue(sa, cfs); CFRelease(cfs); }
                        }
                CFRelease(secrets);
@@ -3417,7 +3493,7 @@ mDNSlocal void SetDomainSecrets(mDNS *m)
                                {
                                ClientTunnel *cur = *ptr;
                                LogOperation("SetDomainSecrets: removing client %##s from list", cur->dstname.c);
-                               if (cur->q.ThisQInterval >= 0)  mDNS_StopQuery(m, &cur->q);
+                               if (cur->q.ThisQInterval >= 0) mDNS_StopQuery(m, &cur->q);
                                AutoTunnelSetKeys(cur, mDNSfalse);
                                *ptr = cur->next;
                                freeL("ClientTunnel", cur);
@@ -3433,9 +3509,9 @@ mDNSlocal void SetDomainSecrets(mDNS *m)
                                {
                                // stop the NAT operation
                                mDNS_StopNATOperation_internal(m, &info->AutoTunnelNAT);
-                               if (info->AutoTunnelHostRecord.namestorage.c[0] && info->AutoTunnelNAT.clientCallback)
+                               if (info->AutoTunnelNAT.clientCallback)
                                        {
-                                       // reset port and let the AutoTunnelNATCallback handle cleanup
+                                       // Reset port and let the AutoTunnelNATCallback handle cleanup
                                        info->AutoTunnelNAT.ExternalAddress = m->ExternalAddress;
                                        info->AutoTunnelNAT.ExternalPort    = zeroIPPort;
                                        info->AutoTunnelNAT.Lifetime        = 0;
@@ -3494,13 +3570,13 @@ mDNSexport void mDNSMacOSXNetworkChanged(mDNS *const m)
        {
        LogOperation("***   Network Configuration Change   ***  (%d)%s",
                m->p->NetworkChanged ? mDNS_TimeNow(m) - m->p->NetworkChanged : 0,
-               m->p->NetworkChanged ? "": " (no scheduled configuration change)");
+               m->p->NetworkChanged ? "" : " (no scheduled configuration change)");
        m->p->NetworkChanged = 0;               // If we received a network change event and deferred processing, we're now dealing with it
        mDNSs32 utc = mDNSPlatformUTC();
        MarkAllInterfacesInactive(m, utc);
        UpdateInterfaceList(m, utc);
-       int nDeletions = ClearInactiveInterfaces(m, utc);
-       int nAdditions = SetupActiveInterfaces(m, utc);
+       ClearInactiveInterfaces(m, utc);
+       SetupActiveInterfaces(m, utc);
 
        #if APPLE_OSX_mDNSResponder
                {
@@ -3521,7 +3597,7 @@ mDNSexport void mDNSMacOSXNetworkChanged(mDNS *const m)
                                mDNSAddr tmpSrc = zeroAddr;
                                mDNSAddr tmpDst = { mDNSAddrType_IPv4, {{{0}}} };
                                tmpDst.ip.v4 = p->rmt_outer;
-                               FindSourceAddrForIP(&tmpDst, &tmpSrc);
+                               mDNSPlatformSourceAddrForDest(&tmpSrc, &tmpDst);
                                if (!mDNSSameIPv6Address(p->loc_inner, m->AutoTunnelHostAddr) ||
                                        !mDNSSameIPv4Address(p->loc_outer, tmpSrc.ip.v4))
                                        {
@@ -3534,8 +3610,7 @@ mDNSexport void mDNSMacOSXNetworkChanged(mDNS *const m)
                }
        #endif APPLE_OSX_mDNSResponder
 
-       uDNS_SetupDNSConfig(m);                           // note - call DynDNSConfigChanged *before* mDNS_UpdateLLQs
-       if (nDeletions || nAdditions) mDNS_UpdateLLQs(m); // so that LLQs are restarted against the up to date name servers
+       uDNS_SetupDNSConfig(m);
 
        if (m->MainCallback)
                m->MainCallback(m, mStatus_ConfigChanged);
@@ -3578,6 +3653,17 @@ mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, v
                m->p->NetworkChanged - NonZeroTime(m->timenow + delay) < 0)
                m->p->NetworkChanged = NonZeroTime(m->timenow + delay);
 
+       // KeyChain frequently fails to notify clients of change events. To work around this
+       // we set a timer and periodically poll to detect if any changes have occurred.
+       // Without this Back To My Mac just does't work for a large number of users.
+       // See <rdar://problem/5124399> Not getting Keychain Changed events when enabling BTMM
+       if (c3 || CFArrayContainsValue(changedKeys, range, NetworkChangedKey_BackToMyMac))
+               {
+               LogOperation("***   NetworkChanged   *** starting KeyChainBugTimer");
+               m->p->KeyChainBugTimer    = NonZeroTime(m->timenow + delay);
+               m->p->KeyChainBugInterval = mDNSPlatformOneSecond;
+               }
+
        if (!m->SuppressSending ||
                m->SuppressSending - m->p->NetworkChanged < 0)
                m->SuppressSending = m->p->NetworkChanged;
@@ -3665,6 +3751,7 @@ mDNSlocal OSStatus KeychainChanged(SecKeychainEvent keychainEvent, SecKeychainCa
                                        keychainEvent == kSecAddEvent    ? "kSecAddEvent" : 
                                        keychainEvent == kSecDeleteEvent ? "kSecDeleteEvent" : 
                                        keychainEvent == kSecUpdateEvent ? "kSecUpdateEvent" :  "<Unknown>");
+                               // We're running on the CFRunLoop (Mach port) thread, not the kqueue thread, so we need to grab the KQueueLock before proceeding
                                KQueueLock(m);
                                mDNS_Lock(m);
                                SetDomainSecrets(m);
@@ -3849,6 +3936,7 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m)
        m->p->userhostlabel.c[0] = 0;
        m->p->usernicelabel.c[0] = 0;
        m->p->NotifyUser         = 0;
+       m->p->KeyChainBugTimer   = 0;
 
        m->AutoTunnelHostAddr.b[0] = 0;         // Zero out AutoTunnelHostAddr so UpdateInterfaceList() know it has to set it up
 
index 6e8f12fa6753c00d9365f7be98d84c61bb23a5a0..25fe6a4df8df118cc9cbe7ca4230bdf38a0ef010 100644 (file)
     Change History (most recent first):
 
 $Log: mDNSMacOSX.h,v $
+Revision 1.74  2007/11/02 20:18:13  cheshire
+<rdar://problem/5575583> BTMM: Work around keychain notification bug <rdar://problem/5124399>
+
+Revision 1.73  2007/10/17 18:42:06  cheshire
+Export SetDomainSecrets so its callable from other files
+
 Revision 1.72  2007/08/01 16:09:14  cheshire
 Removed unused NATTraversalInfo substructure from AuthRecord; reduced structure sizecheck values accordingly
 
@@ -152,6 +158,14 @@ struct mDNS_PlatformSupport_struct
        mDNSs32                  NotifyUser;
        mDNSs32                  HostNameConflict;      // Time we experienced conflict on our link-local host name
        mDNSs32                  NetworkChanged;
+       
+       // KeyChain frequently fails to notify clients of change events. To work around this
+       // we set a timer and periodically poll to detect if any changes have occurred.
+       // Without this Back To My Mac just does't work for a large number of users.
+       // See <rdar://problem/5124399> Not getting Keychain Changed events when enabling BTMM
+       mDNSs32                  KeyChainBugTimer;
+       mDNSs32                  KeyChainBugInterval;
+       
        SCDynamicStoreRef        Store;
        CFRunLoopSourceRef       StoreRLS;
        IONotificationPortRef    PowerPortRef;
@@ -165,6 +179,7 @@ struct mDNS_PlatformSupport_struct
 extern int KQueueFD;
 
 extern void NotifyOfElusiveBug(const char *title, const char *msg);    // Both strings are UTF-8 text
+extern void SetDomainSecrets(mDNS *m);
 extern void mDNSMacOSXNetworkChanged(mDNS *const m);
 extern int mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring);
 
index 91bc819fb48c7819233b6a95b3a01dce8ece09e4..0a4763ff66dfcfa1e99a6ab285eeaf664ef6093f 100755 (executable)
 # then try typing "gmake os=xxx" instead.
 #
 # $Log: Makefile,v $
+# Revision 1.78  2007/10/22 20:16:49  cheshire
+# Got rid of jaguar and panther from list of target platforms;
+# changed "os=tiger" to "os=x" (which works with both Tiger and Leopard)
+#
+# Revision 1.77  2007/10/22 20:04:51  cheshire
+# Need to include PlatformCommon.c.o in embedded targets
+#
 # Revision 1.76  2007/07/31 23:39:02  mcguire
 # Don't bail on errors in flex-generated .c files
 #
@@ -367,25 +374,7 @@ CFLAGS_OS = -DHAVE_BROKEN_RECVDSTADDR
 LDCONFIG = ldconfig
 else
 
-ifeq ($(os),jaguar)
-CFLAGS_OS = -DHAVE_IPV6 -no-cpp-precomp -Werror -DNOT_HAVE_SOCKLEN_T
-LD = libtool -dynamic
-LINKOPTS = -lSystem
-LDSUFFIX = dylib
-JDK = /System/Library/Frameworks/JavaVM.framework/Home
-JAVACFLAGS_OS = -dynamiclib -I/System/Library/Frameworks/JavaVM.framework/Headers -framework JavaVM 
-else
-
-ifeq ($(os),panther)
-CFLAGS_OS = -DHAVE_IPV6 -no-cpp-precomp -Werror
-LD = libtool -dynamic
-LINKOPTS = -lSystem
-LDSUFFIX = dylib
-JDK = /System/Library/Frameworks/JavaVM.framework/Home
-JAVACFLAGS_OS = -dynamiclib -I/System/Library/Frameworks/JavaVM.framework/Headers -framework JavaVM 
-else
-
-ifeq ($(os),tiger)
+ifeq ($(os),x)
 CFLAGS_OS = -DHAVE_IPV6 -no-cpp-precomp -Werror -Wdeclaration-after-statement #-Wunreachable-code
 CC = @gcc-4.0
 LD = $(CC) -dynamiclib
@@ -395,10 +384,8 @@ JDK = /System/Library/Frameworks/JavaVM.framework/Home
 JAVACFLAGS_OS = -dynamiclib -I/System/Library/Frameworks/JavaVM.framework/Headers -framework JavaVM 
 else
 
-$(error ERROR: Must specify target OS on command-line, e.g. "make os=tiger [target]".\
-Supported operating systems include: jaguar, panther, tiger, linux, netbsd, freebsd, openbsd, solaris) 
-endif
-endif
+$(error ERROR: Must specify target OS on command-line, e.g. "make os=x [target]".\
+Supported operating systems include: x, linux, netbsd, freebsd, openbsd, solaris) 
 endif
 endif
 endif
@@ -661,7 +648,8 @@ JavaDoc: Java setup
 #############################################################################
 
 # The following targets build embedded example programs
-SPECIALOBJ = $(OBJDIR)/mDNSPosix.c.o $(OBJDIR)/mDNSUNP.c.o $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/GenLinkedList.c.o $(OBJDIR)/DNSDigest.c.o $(OBJDIR)/uDNS.c.o $(OBJDIR)/DNSCommon.c.o
+SPECIALOBJ = $(OBJDIR)/mDNSPosix.c.o $(OBJDIR)/mDNSUNP.c.o $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/GenLinkedList.c.o \
+       $(OBJDIR)/DNSDigest.c.o $(OBJDIR)/uDNS.c.o $(OBJDIR)/DNSCommon.c.o $(OBJDIR)/PlatformCommon.c.o
 COMMONOBJ  = $(SPECIALOBJ) $(OBJDIR)/mDNS.c.o
 APPOBJ     = $(COMMONOBJ) $(OBJDIR)/ExampleClientApp.c.o
 
index 741ca596fab4221fde572d7bb4e66ec2273489bd..be0523f86df6c54c51a07ca9dcea96b94524df72 100644 (file)
@@ -21,6 +21,9 @@
        Change History (most recent first):
 
 $Log: PosixDaemon.c,v $
+Revision 1.43  2007/10/22 20:05:34  cheshire
+Use mDNSPlatformSourceAddrForDest instead of FindSourceAddrForIP
+
 Revision 1.42  2007/09/18 19:09:02  cheshire
 <rdar://problem/5489549> mDNSResponderHelper (and other binaries) missing SCCS version strings
 
@@ -119,11 +122,12 @@ mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result)
 static void Reconfigure(mDNS *m)
        {
        mDNSAddr DynDNSIP;
+       const mDNSAddr dummy = { mDNSAddrType_IPv4, { { { 1, 1, 1, 1 } } } };;
        mDNS_SetPrimaryInterfaceInfo(m, NULL, NULL, NULL);
        if (ParseDNSServers(m, uDNS_SERVERS_FILE) < 0)
                LogMsg("Unable to parse DNS server list. Unicast DNS-SD unavailable");
        ReadDDNSSettingsFromConfFile(m, CONFIG_FILE, &DynDNSHostname, &DynDNSZone, NULL);
-       FindSourceAddrForIP(NULL, &DynDNSIP);
+       mDNSPlatformSourceAddrForDest(&DynDNSIP, &dummy);
        if (DynDNSHostname.c[0]) mDNS_AddDynDNSHostName(m, &DynDNSHostname, NULL, NULL);
        if (DynDNSIP.type)       mDNS_SetPrimaryInterfaceInfo(m, &DynDNSIP, NULL, NULL);
        m->MainCallback(m, mStatus_ConfigChanged);
index 8ca7ec4402574c6931d5ec1b65a5e81b36925fba..08b4945bbd8012712b2f69c1e74ead293ae88239 100644 (file)
     Change History (most recent first):
 
 $Log: PlatformCommon.c,v $
+Revision 1.13  2007/10/22 20:07:07  cheshire
+Moved mDNSPlatformSourceAddrForDest from mDNSMacOSX.c to PlatformCommon.c so
+Posix build can share the code (better than just pasting it into mDNSPosix.c)
+
+Revision 1.12  2007/10/16 17:19:53  cheshire
+<rdar://problem/3557903> Performance: Core code will not work on platforms with small stacks
+Cut ReadDDNSSettingsFromConfFile stack from 2112 to 1104 bytes
+
 Revision 1.11  2007/07/31 23:08:34  mcguire
 <rdar://problem/5329542> BTMM: Make AutoTunnel mode work with multihoming
 
@@ -58,7 +66,7 @@ Move ReadDDNSSettingsFromConfFile() from mDNSMacOSX.c to PlatformCommon.c
 #include <stdio.h>                             // Needed for fopen() etc.
 #include <unistd.h>                            // Needed for close()
 #include <string.h>                            // Needed for strlen() etc.
-#include <errno.h>                     // Needed for errno etc.
+#include <errno.h>                             // Needed for errno etc.
 #include <sys/socket.h>                        // Needed for socket() etc.
 #include <netinet/in.h>                        // Needed for sockaddr_in
 
@@ -71,23 +79,42 @@ Move ReadDDNSSettingsFromConfFile() from mDNSMacOSX.c to PlatformCommon.c
 #endif
 
 // Bind a UDP socket to find the source address to a destination
-mDNSexport void FindSourceAddrForIP(mDNSAddr *const dst, mDNSAddr *src)
+mDNSexport void mDNSPlatformSourceAddrForDest(mDNSAddr *const src, const mDNSAddr *const dst)
        {
-       struct sockaddr_in addr;
+       union { struct sockaddr s; struct sockaddr_in a4; struct sockaddr_in6 a6; } addr;
        socklen_t len = sizeof(addr);
-       int sock = socket(AF_INET,SOCK_DGRAM,0);
+       int sock = socket(AF_INET, SOCK_DGRAM, 0);
        src->type = mDNSAddrType_None;
        if (sock == -1) return;
-       if (dst->type != mDNSAddrType_IPv4) return; // Only support v4 for now
-       addr.sin_family = AF_INET;
-       addr.sin_port = 1;      // Not important, any port will do
-       if (dst == NULL) addr.sin_addr.s_addr = 0x11111111;
-       else addr.sin_addr.s_addr = dst->ip.v4.NotAnInteger;
-       if ((connect(sock,(const struct sockaddr*)&addr,sizeof(addr))) == -1) { close(sock); return; }
-       if ((getsockname(sock,(struct sockaddr*)&addr, &len)) == -1) { close(sock); return; }
+       if (dst->type == mDNSAddrType_IPv4)
+               {
+               addr.a4.sin_len         = sizeof(addr.a4);
+               addr.a4.sin_family      = AF_INET;
+               addr.a4.sin_port        = 1;    // Not important, any port will do
+               addr.a4.sin_addr.s_addr = dst->ip.v4.NotAnInteger;
+               }
+       else if (dst->type == mDNSAddrType_IPv6)
+               {
+               addr.a6.sin6_len      = sizeof(addr.a6);
+               addr.a6.sin6_family   = AF_INET6;
+               addr.a6.sin6_flowinfo = 0;
+               addr.a6.sin6_port     = 1;      // Not important, any port will do
+               addr.a6.sin6_addr     = *(struct in6_addr*)&dst->ip.v6;
+               addr.a6.sin6_scope_id = 0;
+               }
+       else return;
+       
+       if ((connect(sock, &addr.s, addr.s.sa_len)) < 0)
+               { LogMsg("mDNSPlatformSourceAddrForDest: connect %#a failed errno %d (%s)", dst, errno, strerror(errno)); goto exit; }
+
+       if ((getsockname(sock, &addr.s, &len)) < 0)
+               { LogMsg("mDNSPlatformSourceAddrForDest: getsockname failed errno %d (%s)", errno, strerror(errno)); goto exit; }
+
+       src->type = dst->type;
+       if (dst->type == mDNSAddrType_IPv4) src->ip.v4.NotAnInteger = addr.a4.sin_addr.s_addr;
+       else                                src->ip.v6 = *(mDNSv6Addr*)&addr.a6.sin6_addr;
+exit:
        close(sock);
-       src->type = mDNSAddrType_IPv4;
-       src->ip.v4.NotAnInteger = addr.sin_addr.s_addr;
        }
 
 // dst must be at least MAX_ESCAPED_DOMAIN_NAME bytes, and option must be less than 32 bytes in length
@@ -114,8 +141,7 @@ mDNSlocal mDNSBool GetConfigOption(char *dst, const char *option, FILE *f)
 
 mDNSexport void ReadDDNSSettingsFromConfFile(mDNS *const m, const char *const filename, domainname *const hostname, domainname *const domain, mDNSBool *DomainDiscoveryDisabled)
        {
-       char buf   [MAX_ESCAPED_DOMAIN_NAME];
-       char secret[MAX_ESCAPED_DOMAIN_NAME] = "";
+       char buf[MAX_ESCAPED_DOMAIN_NAME] = "";
        mStatus err;
        FILE *f = fopen(filename, "r");
 
@@ -128,7 +154,8 @@ mDNSexport void ReadDDNSSettingsFromConfFile(mDNS *const m, const char *const fi
                if (DomainDiscoveryDisabled && GetConfigOption(buf, "DomainDiscoveryDisabled", f) && !strcasecmp(buf, "true")) *DomainDiscoveryDisabled = mDNStrue;
                if (hostname && GetConfigOption(buf, "hostname", f) && !MakeDomainNameFromDNSNameString(hostname, buf)) goto badf;
                if (domain && GetConfigOption(buf, "zone", f) && !MakeDomainNameFromDNSNameString(domain, buf)) goto badf;
-               GetConfigOption(secret, "secret-64", f);  // failure means no authentication                    
+               buf[0] = 0;
+               GetConfigOption(buf, "secret-64", f);  // failure means no authentication                       
                fclose(f);
                f = NULL;
                }
@@ -138,11 +165,11 @@ mDNSexport void ReadDDNSSettingsFromConfFile(mDNS *const m, const char *const fi
                return;
                }
 
-       if (domain && domain->c[0] && secret[0])
+       if (domain && domain->c[0] && buf[0])
                {
                DomainAuthInfo *info = (DomainAuthInfo*)mDNSPlatformMemAllocate(sizeof(*info));
                // for now we assume keyname = service reg domain and we use same key for service and hostname registration
-               err = mDNS_SetSecretForDomain(m, info, domain, domain, secret, mDNSfalse);
+               err = mDNS_SetSecretForDomain(m, info, domain, domain, buf, mDNSfalse);
                if (err) LogMsg("ERROR: mDNS_SetSecretForDomain returned %d for domain %##s", err, domain->c);
                }
 
index 4007ad3294b9ac79320826f4ab905fbc5fe11c8c..44beb1c0ea71838529ed5d751ad12682afe25f4f 100644 (file)
@@ -17,6 +17,9 @@
     Change History (most recent first):
 
 $Log: PlatformCommon.h,v $
+Revision 1.8  2007/10/22 20:07:52  cheshire
+Deleted unused FindSourceAddrForIP() function
+
 Revision 1.7  2007/07/31 23:08:34  mcguire
 <rdar://problem/5329542> BTMM: Make AutoTunnel mode work with multihoming
 
@@ -41,5 +44,4 @@ Move ReadDDNSSettingsFromConfFile() from mDNSMacOSX.c to PlatformCommon.c
 
  */
 
-extern void FindSourceAddrForIP(mDNSAddr *const dst, mDNSAddr *src);
 extern void ReadDDNSSettingsFromConfFile(mDNS *const m, const char *const filename, domainname *const hostname, domainname *const domain, mDNSBool *DomainDiscoveryDisabled);
index a3ee799899782c25dc87306581ec76cc74119264..de5527759cbf2639a004919e1b3180fcc8154ab9 100644 (file)
@@ -77,7 +77,7 @@
  */
 
 #ifndef _DNS_SD_H
-#define _DNS_SD_H 1610100
+#define _DNS_SD_H 1640000
 
 #ifdef  __cplusplus
     extern "C" {
index 38f1d3df4b377c0f36e73ee8b7b989bb1ac9886c..a3860f22bd84ae10f8714423a91f401781ae103b 100644 (file)
     Change History (most recent first):
 
 $Log: dnsextd.c,v $
+Revision 1.84  2007/10/24 18:19:37  cheshire
+Fixed header byte order bug sending update responses
+
+Revision 1.83  2007/10/17 22:52:26  cheshire
+Get rid of unused mDNS_UpdateLLQs()
+
 Revision 1.82  2007/09/27 17:42:49  cheshire
 Fix naming: for consistency, "kDNSFlag1_RC" should be "kDNSFlag1_RC_Mask"
 
@@ -1602,6 +1608,7 @@ mDNSlocal PktMsg *FormatLeaseReply(DaemonInfo *d, PktMsg *orig, mDNSu32 lease)
        ptr = putUpdateLease(&reply->msg, ptr, lease);
        if (!ptr) { Log("FormatLeaseReply: putUpdateLease failed"); free(reply); return NULL; }
        reply->len = ptr - (mDNSu8 *)&reply->msg;
+       HdrHToN(reply);
        return reply;
        }
 
@@ -1627,7 +1634,7 @@ HandleRequest
                int i, adds = 0, dels = 0;
                const mDNSu8 *ptr, *end = (mDNSu8 *)&request->msg + request->len;
                HdrNToH(request);
-               lease = GetPktLease(NULL, &request->msg, end);
+               lease = GetPktLease(&mDNSStorage, &request->msg, end);
                ptr = LocateAuthorities(&request->msg, end);
                for (i = 0; i < request->msg.h.mDNS_numUpdates; i++)
                        {
@@ -3236,7 +3243,6 @@ mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info,
        const domainname *domain, const domainname *keyname, const char *b64keydata, mDNSBool AutoTunnel)
        { ( void ) m; ( void ) info; ( void ) domain; ( void ) keyname; ( void ) b64keydata; ( void ) AutoTunnel; return 0; }
 mStatus mDNS_StopQuery(mDNS *const m, DNSQuestion *const question) { ( void ) m; ( void ) question; return 0; }
-void mDNS_UpdateLLQs(mDNS * const m) { ( void ) m; }
 mDNS mDNSStorage;
 
 
index 604f39b7b094217e20981b2c540f19bf75663e24..201a44b0ab338d27de549a521d7089c6b43bbf42 100644 (file)
        Change History (most recent first):
 
 $Log: uds_daemon.c,v $
+Revision 1.379  2007/11/01 19:32:14  cheshire
+Added "DEBUG_64BIT_SCM_RIGHTS" debugging code
+
+Revision 1.378  2007/10/31 19:21:40  cheshire
+Don't show Expire time for records and services that aren't currently registered
+
+Revision 1.377  2007/10/30 23:48:20  cheshire
+Improved SIGINFO listing of question state
+
+Revision 1.376  2007/10/30 20:43:54  cheshire
+Fixed compiler warning when LogAllOperations is turned off
+
+Revision 1.375  2007/10/26 22:51:38  cheshire
+Improved SIGINFO output to show timers for AuthRecords and ServiceRegistrations
+
+Revision 1.374  2007/10/25 22:45:02  cheshire
+Tidied up code for DNSServiceRegister callback status messages
+
+Revision 1.373  2007/10/25 21:28:43  cheshire
+Add ServiceRegistrations to SIGINFO output
+
+Revision 1.372  2007/10/25 21:21:45  cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+Don't unlink_and_free_service_instance at the first error
+
+Revision 1.371  2007/10/18 23:34:40  cheshire
+<rdar://problem/5532821> Need "considerable burden on the network" warning in uds_daemon.c
+
+Revision 1.370  2007/10/17 18:44:23  cheshire
+<rdar://problem/5539930> Goodbye packets not being sent for services on shutdown
+
+Revision 1.369  2007/10/16 17:18:27  cheshire
+Fixed Posix compile errors
+
+Revision 1.368  2007/10/16 16:58:58  cheshire
+Improved debugging error messages in read_msg()
+
+Revision 1.367  2007/10/15 22:55:14  cheshire
+Make read_msg return "void" (since request_callback just ignores the redundant return value anyway)
+
 Revision 1.366  2007/10/10 00:48:54  cheshire
 <rdar://problem/5526379> Daemon spins in an infinite loop when it doesn't get the control message it's expecting
 
@@ -778,6 +818,7 @@ struct request_state
                        DNSQuestion qsrv;
                        const ResourceRecord *txt;
                        const ResourceRecord *srv;
+                       mDNSs32 ReportTime;
                        } resolve;
                 ;
                } u;
@@ -1168,6 +1209,12 @@ mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, m
        mDNSBool SuppressError = mDNSfalse;
        service_instance *instance = srs->ServiceContext;
        reply_state         *rep;
+#if LogAllOperations || MDNS_DEBUGMSGS
+       char *fmt = (result == mStatus_NoError)      ? "%3d: DNSServiceRegister(%##s, %u) REGISTERED"    :
+                               (result == mStatus_MemFree)      ? "%3d: DNSServiceRegister(%##s, %u) DEREGISTERED"  :
+                               (result == mStatus_NameConflict) ? "%3d: DNSServiceRegister(%##s, %u) NAME CONFLICT" :
+                                                                  "%3d: DNSServiceRegister(%##s, %u) %s %d";
+#endif
        (void)m; // Unused
        if (!srs)      { LogMsg("regservice_callback: srs is NULL %d",                 result); return; }
        if (!instance) { LogMsg("regservice_callback: srs->ServiceContext is NULL %d", result); return; }
@@ -1178,14 +1225,7 @@ mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, m
                !instance->default_local)
                SuppressError = mDNStrue;
 
-       if (result == mStatus_NoError)
-               LogOperation("%3d: DNSServiceRegister(%##s, %u) REGISTERED",    instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port));
-       else if (result == mStatus_MemFree)
-               LogOperation("%3d: DNSServiceRegister(%##s, %u) DEREGISTERED",  instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port));
-       else if (result == mStatus_NameConflict)
-               LogOperation("%3d: DNSServiceRegister(%##s, %u) NAME CONFLICT", instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port));
-       else
-               LogOperation("%3d: DNSServiceRegister(%##s, %u) CALLBACK %d",   instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port), result);
+       LogOperation(fmt, instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port), SuppressError ? "suppressed error" : "CALLBACK", result);
 
        if (!instance->request && result != mStatus_MemFree) { LogMsg("regservice_callback: instance->request is NULL %d", result); return; }
 
@@ -1249,15 +1289,12 @@ mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, m
                }
        else
                {
-               if (result != mStatus_NATTraversal)
-                       LogMsg("regservice_callback: Error %d%s for %s", result, SuppressError ? " (suppressed)" : "", ARDisplayString(m, &srs->RR_SRV));
                if (!SuppressError) 
                        {
                        if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, kDNSServiceFlagsAdd, result) != mStatus_NoError)
                                LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->sd, srs->RR_SRV.resrec.name->c);
                        else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; }
                        }
-               unlink_and_free_service_instance(instance);
                }
        }
 
@@ -2044,7 +2081,16 @@ mDNSlocal void udsserver_automatic_browse_domain_changed(const DNameListElem *co
 mDNSlocal void FreeARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
        {
        (void)m;  // unused
-       if (result == mStatus_MemFree) mDNSPlatformMemFree(rr->RecordContext);
+       if (result == mStatus_MemFree)
+               {
+               // On shutdown, mDNS_Close automatically deregisters all records
+               // Since in this case no one has called DeregisterLocalOnlyDomainEnumPTR to cut the record
+               // from the LocalDomainEnumRecords list, we do this here before we free the memory.
+               ARListElem **ptr = &LocalDomainEnumRecords;
+               while (*ptr && &(*ptr)->ar != rr) ptr = &(*ptr)->next;
+               if (*ptr) *ptr = (*ptr)->next;
+               mDNSPlatformMemFree(rr->RecordContext);
+               }
        }
 
 mDNSlocal void RegisterLocalOnlyDomainEnumPTR(mDNS *m, const domainname *d, int type)
@@ -2457,6 +2503,7 @@ mDNSlocal mStatus handle_resolve_request(request_state *request)
        request->u.resolve.qtxt.ReturnIntermed   = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
        request->u.resolve.qtxt.QuestionCallback = resolve_result_callback;
        request->u.resolve.qtxt.QuestionContext  = request;
+       request->u.resolve.ReportTime            = NonZeroTime(mDNS_TimeNow(&mDNSStorage) + 130 * mDNSPlatformOneSecond);
 
        // ask the questions
        LogOperation("%3d: DNSServiceResolve(%##s) START", request->sd, request->u.resolve.qsrv.qname.c);
@@ -3006,71 +3053,50 @@ mDNSlocal request_state *NewRequest(void)
        }
 
 // read_msg may be called any time when the transfer state (req->ts) is t_morecoming.
-// returns the current state of the request (morecoming, error, complete, terminated.)
 // if there is no data on the socket, the socket will be closed and t_terminated will be returned
-// *** NOTE return value is actually ignored -- should change return type to void ***
-mDNSlocal int read_msg(request_state *req)
+mDNSlocal void read_msg(request_state *req)
        {
-       mDNSu32 nleft;
-       int nread;
-
        if (req->ts == t_terminated || req->ts == t_error)
-               {
-               LogMsg("ERROR: read_msg called with transfer state terminated or error");
-               req->ts = t_error;
-               return t_error;
-               }
+               { LogMsg("%3d: ERROR: read_msg called with transfer state terminated or error", req->sd); req->ts = t_error; return; }
 
        if (req->ts == t_complete)      // this must be death or something is wrong
                {
                char buf[4];    // dummy for death notification
-               nread = recv(req->sd, buf, 4, 0);
-               if (!nread) { req->ts = t_terminated; return t_terminated; }
+               int nread = recv(req->sd, buf, 4, 0);
+               if (!nread) { req->ts = t_terminated; return; }
                if (nread < 0) goto rerror;
-               LogMsg("ERROR: read data from a completed request.");
+               LogMsg("%3d: ERROR: read data from a completed request", req->sd);
                req->ts = t_error;
-               return t_error;
+               return;
                }
 
        if (req->ts != t_morecoming)
-               {
-               LogMsg("ERROR: read_msg called with invalid transfer state (%d)", req->ts);
-               req->ts = t_error;
-               return t_error;
-               }
+               { LogMsg("%3d: ERROR: read_msg called with invalid transfer state (%d)", req->sd, req->ts); req->ts = t_error; return; }
 
        if (req->hdr_bytes < sizeof(ipc_msg_hdr))
                {
-               nleft = sizeof(ipc_msg_hdr) - req->hdr_bytes;
-               nread = recv(req->sd, (char *)&req->hdr + req->hdr_bytes, nleft, 0);
-               if (nread == 0) { req->ts = t_terminated; return t_terminated; }
+               mDNSu32 nleft = sizeof(ipc_msg_hdr) - req->hdr_bytes;
+               int nread = recv(req->sd, (char *)&req->hdr + req->hdr_bytes, nleft, 0);
+               if (nread == 0) { req->ts = t_terminated; return; }
                if (nread < 0) goto rerror;
                req->hdr_bytes += nread;
                if (req->hdr_bytes > sizeof(ipc_msg_hdr))
-                       {
-                       LogMsg("ERROR: read_msg - read too many header bytes");
-                       req->ts = t_error;
-                       return t_error;
-                       }
+                       { LogMsg("%3d: ERROR: read_msg - read too many header bytes", req->sd); req->ts = t_error; return; }
 
                // only read data if header is complete
                if (req->hdr_bytes == sizeof(ipc_msg_hdr))
                        {
                        ConvertHeaderBytes(&req->hdr);
                        if (req->hdr.version != VERSION)
-                               { LogMsg("ERROR: client version 0x%08X daemon version 0x%08X", req->hdr.version, VERSION); req->ts = t_error; return t_error; }
+                               { LogMsg("%3d: ERROR: client version 0x%08X daemon version 0x%08X", req->sd, req->hdr.version, VERSION); req->ts = t_error; return; }
 
                        // Largest conceivable single request is a DNSServiceRegisterRecord() or DNSServiceAddRecord()
                        // with 64kB of rdata. Adding 1005 byte for a maximal domain name, plus a safety margin
                        // for other overhead, this means any message above 70kB is definitely bogus.
                        if (req->hdr.datalen > 70000)
-                               {
-                               LogMsg("ERROR: read_msg - hdr.datalen %lu (%X) > 70000", req->hdr.datalen, req->hdr.datalen);
-                               req->ts = t_error;
-                               return t_error;
-                               }
+                               { LogMsg("%3d: ERROR: read_msg - hdr.datalen %lu (%X) > 70000", req->sd, req->hdr.datalen, req->hdr.datalen); req->ts = t_error; return; }
                        req->msgbuf = mallocL("request_state msgbuf", req->hdr.datalen + MSG_PAD_BYTES);
-                       if (!req->msgbuf) { my_perror("ERROR: malloc"); req->ts = t_error; return t_error; }
+                       if (!req->msgbuf) { my_perror("ERROR: malloc"); req->ts = t_error; return; }
                        req->msgptr = req->msgbuf;
                        req->msgend = req->msgbuf + req->hdr.datalen;
                        mDNSPlatformMemZero(req->msgbuf, req->hdr.datalen + MSG_PAD_BYTES);
@@ -3083,11 +3109,12 @@ mDNSlocal int read_msg(request_state *req)
        // (even if only the one-byte empty C string placeholder for the old ctrl_path parameter)
        if (req->hdr_bytes == sizeof(ipc_msg_hdr) && req->data_bytes < req->hdr.datalen)
                {
-               nleft = req->hdr.datalen - req->data_bytes;
+               mDNSu32 nleft = req->hdr.datalen - req->data_bytes;
+               int nread;
                struct iovec vec = { req->msgbuf + req->data_bytes, nleft };    // Tell recvmsg where we want the bytes put
                struct msghdr msg;
                struct cmsghdr *cmsg;
-               char cbuf[sizeof(struct cmsghdr) + sizeof(dnssd_sock_t)];
+               char cbuf[CMSG_SPACE(sizeof(dnssd_sock_t))];
                msg.msg_name       = 0;
                msg.msg_namelen    = 0;
                msg.msg_iov        = &vec;
@@ -3096,28 +3123,31 @@ mDNSlocal int read_msg(request_state *req)
                msg.msg_controllen = sizeof(cbuf);
                msg.msg_flags      = 0;
                nread = recvmsg(req->sd, &msg, 0);
-               if (nread == 0) { req->ts = t_terminated; return t_terminated; }
+               if (nread == 0) { req->ts = t_terminated; return; }
                if (nread < 0) goto rerror;
                req->data_bytes += nread;
                if (req->data_bytes > req->hdr.datalen)
-                       {
-                       LogMsg("ERROR: read_msg - read too many data bytes");
-                       req->ts = t_error;
-                       return t_error;
-                       }
+                       { LogMsg("%3d: ERROR: read_msg - read too many data bytes", req->sd); req->ts = t_error; return; }
                cmsg = CMSG_FIRSTHDR(&msg);
+#if DEBUG_64BIT_SCM_RIGHTS
+               LogMsg("%3d: Expecting %d %d %d %d", req->sd, sizeof(cbuf),       sizeof(cbuf),   SOL_SOCKET,       SCM_RIGHTS);
+               LogMsg("%3d: Got       %d %d %d %d", req->sd, msg.msg_controllen, cmsg->cmsg_len, cmsg->cmsg_level, cmsg->cmsg_type);
+#endif DEBUG_64BIT_SCM_RIGHTS
                if (msg.msg_controllen == sizeof(cbuf) &&
                        cmsg->cmsg_len     == sizeof(cbuf) &&
                        cmsg->cmsg_level   == SOL_SOCKET   &&
                        cmsg->cmsg_type    == SCM_RIGHTS)
                        {
                        req->errsd = *(dnssd_sock_t *)CMSG_DATA(cmsg);
+#if DEBUG_64BIT_SCM_RIGHTS
+                       LogMsg("%3d: read req->errsd %d", req->sd, req->errsd);
+#endif DEBUG_64BIT_SCM_RIGHTS
                        if (req->data_bytes < req->hdr.datalen)
                                {
                                LogMsg("%3d: Client sent error socket %d via SCM_RIGHTS with req->data_bytes %d < req->hdr.datalen %d",
                                        req->sd, req->errsd, req->data_bytes, req->hdr.datalen);
                                req->ts = t_error;
-                               return t_error;
+                               return;
                                }
                        }
                }
@@ -3148,27 +3178,27 @@ mDNSlocal int read_msg(request_state *req)
                        if (ctrl_path[0] == 0)
                                {
                                if (req->errsd == req->sd)
-                                       { LogMsg("%3d: request_callback: ERROR failed to get errsd via SCM_RIGHTS", req->sd); req->ts = t_error; return t_error; }
+                                       { LogMsg("%3d: read_msg: ERROR failed to get errsd via SCM_RIGHTS", req->sd); req->ts = t_error; return; }
                                goto got_errfd;
                                }
 #endif
        
                        req->errsd = socket(AF_DNSSD, SOCK_STREAM, 0);
-                       if (!dnssd_SocketValid(req->errsd)) { my_perror("ERROR: socket"); req->ts = t_error; return t_error; }
-       
+                       if (!dnssd_SocketValid(req->errsd)) { my_perror("ERROR: socket"); req->ts = t_error; return; }
+
                        if (connect(req->errsd, (struct sockaddr *)&cliaddr, sizeof(cliaddr)) < 0)
                                {
 #if !defined(USE_TCP_LOOPBACK)
                                struct stat sb;
-                               LogMsg("request_callback: Couldn't connect to error return path socket “%s” errno %d %s",
-                                       cliaddr.sun_path, dnssd_errno(), dnssd_strerror(dnssd_errno()));
+                               LogMsg("%3d: read_msg: Couldn't connect to error return path socket “%s” errno %d %s",
+                                       req->sd, cliaddr.sun_path, dnssd_errno(), dnssd_strerror(dnssd_errno()));
                                if (stat(cliaddr.sun_path, &sb) < 0)
-                                       LogMsg("request_callback: stat failed “%s” errno %d %s", cliaddr.sun_path, dnssd_errno(), dnssd_strerror(dnssd_errno()));
+                                       LogMsg("%3d: read_msg: stat failed “%s” errno %d %s", req->sd, cliaddr.sun_path, dnssd_errno(), dnssd_strerror(dnssd_errno()));
                                else
-                                       LogMsg("request_callback: file “%s” mode %o (octal) uid %d gid %d", cliaddr.sun_path, sb.st_mode, sb.st_uid, sb.st_gid);
+                                       LogMsg("%3d: read_msg: file “%s” mode %o (octal) uid %d gid %d", req->sd, cliaddr.sun_path, sb.st_mode, sb.st_uid, sb.st_gid);
 #endif
                                req->ts = t_error;
-                               return t_error;
+                               return;
                                }
        
 got_errfd:
@@ -3178,19 +3208,23 @@ got_errfd:
 #else
                        if (fcntl(req->errsd, F_SETFL, fcntl(req->errsd, F_GETFL, 0) | O_NONBLOCK) != 0)
 #endif
-                               { my_perror("ERROR: could not set control socket to non-blocking mode"); req->ts = t_error; return t_error; }
+                               {
+                               LogMsg("%3d: ERROR: could not set control socket to non-blocking mode errno %d %s",
+                                       req->sd, dnssd_errno(), dnssd_strerror(dnssd_errno()));
+                               req->ts = t_error;
+                               return;
+                               }
                        }
                
                req->ts = t_complete;
                }
 
-       return req->ts;
+       return;
 
 rerror:
-       if (dnssd_errno() == dnssd_EWOULDBLOCK || dnssd_errno() == dnssd_EINTR) return t_morecoming;
-       my_perror("ERROR: read_msg");
+       if (dnssd_errno() == dnssd_EWOULDBLOCK || dnssd_errno() == dnssd_EINTR) return;
+       LogMsg("%3d: ERROR: read_msg errno %d %s", req->sd, dnssd_errno(), dnssd_strerror(dnssd_errno()));
        req->ts = t_error;
-       return t_error;
        }
 
 #define RecordOrientedOp(X) \
@@ -3216,8 +3250,7 @@ mDNSlocal void request_callback(int fd, short filter, void *info)
 
        if (req->hdr.version != VERSION)
                {
-               LogMsg("ERROR: client incompatible with daemon (client version = %d, "
-                          "daemon version = %d)\n", req->hdr.version, VERSION);
+               LogMsg("ERROR: client version %d incompatible with daemon version %d", req->hdr.version, VERSION);
                AbortUnlinkAndFree(req);
                return;
                }
@@ -3630,8 +3663,27 @@ mDNSexport void udsserver_info(mDNS *const m)
        else
                {
                AuthRecord *ar;
+               LogMsgNoIdent("    Int    Next  Expire   State");
                for (ar = m->ResourceRecords; ar; ar=ar->next)
-                       LogMsgNoIdent("%s", ARDisplayString(m, ar));
+                       LogMsgNoIdent("%7d %7d %7d %7d %s",
+                               ar->ThisAPInterval / mDNSPlatformOneSecond,
+                               AuthRecord_uDNS(ar) || ar->AnnounceCount ? (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond : 0,
+                               AuthRecord_uDNS(ar) && ar->expire        ? (ar->expire - now) / mDNSPlatformOneSecond : 0,
+                               ar->state, ARDisplayString(m, ar));
+               }
+
+       LogMsgNoIdent("----- ServiceRegistrations -----");
+       if (!m->ServiceRegistrations) LogMsgNoIdent("<None>");
+       else
+               {
+               ServiceRecordSet *s;
+               LogMsgNoIdent("    Int    Next  Expire   State");
+               for (s = m->ServiceRegistrations; s; s = s->uDNS_next)
+                       LogMsgNoIdent("%7d %7d %7d %7d %s",
+                               s->RR_SRV.ThisAPInterval / mDNSPlatformOneSecond,
+                               (s->RR_SRV.LastAPTime + s->RR_SRV.ThisAPInterval - now) / mDNSPlatformOneSecond,
+                               s->RR_SRV.expire ? (s->RR_SRV.expire - now) / mDNSPlatformOneSecond : 0,
+                               s->state, ARDisplayString(m, &s->RR_SRV));
                }
 
        LogMsgNoIdent("---------- Questions -----------");
@@ -3641,7 +3693,7 @@ mDNSexport void udsserver_info(mDNS *const m)
                DNSQuestion *q;
                CacheUsed = 0;
                CacheActive = 0;
-               LogMsgNoIdent("   Int  Next if    T NumAns Type  Name");
+               LogMsgNoIdent("   Int  Next if    T  NumAns Type  Name");
                for (q = m->Questions; q; q=q->next)
                        {
                        mDNSs32 i = q->ThisQInterval / mDNSPlatformOneSecond;
@@ -3649,10 +3701,11 @@ mDNSexport void udsserver_info(mDNS *const m)
                        NetworkInterfaceInfo *info = (NetworkInterfaceInfo *)q->InterfaceID;
                        CacheUsed++;
                        if (q->ThisQInterval) CacheActive++;
-                       LogMsgNoIdent("%6d%6d %-6s%s %5d  %-6s%##s%s",
+                       LogMsgNoIdent("%6d%6d %-6s%s%s %5d  %-6s%##s%s",
                                i, n,
                                info ? info->ifname : mDNSOpaque16IsZero(q->TargetQID) ? "" : "-U-",
                                mDNSOpaque16IsZero(q->TargetQID) ? " " : q->LongLived ? "L" : "O", // mDNS, long-lived, or one-shot query?
+                               q->AuthInfo    ? "P" : " ",
                                q->CurrentAnswers,
                                DNSTypeName(q->qtype), q->qname.c, q->DuplicateOf ? " (dup)" : "");
                        usleep(1000);   // Limit rate a little so we don't flood syslog too fast
@@ -3807,6 +3860,14 @@ mDNSexport mDNSs32 udsserver_idle(mDNSs32 nextevent)
 
        while (*req)
                {
+               if ((*req)->terminate == resolve_termination_callback)
+                       if ((*req)->u.resolve.ReportTime && now - (*req)->u.resolve.ReportTime >= 0)
+                               {
+                               (*req)->u.resolve.ReportTime = 0;
+                               LogMsgNoIdent("Client application bug: DNSServiceResolver(%##s) active for over two minutes. "
+                                       "This places considerable burden on the network.", (*req)->u.resolve.qsrv.qname.c);
+                               }
+
                while ((*req)->replies)         // Send queued replies
                        {
                        transfer_state result;
index 1241a6faf1f81096ff2a3d81982f3bf195f3aa4c..13b04048bb5e64bc1d55435f72f180530679e5ff 100755 (executable)
@@ -17,6 +17,9 @@
     Change History (most recent first):
     
 $Log: mDNSWin32.c,v $
+Revision 1.129  2007/10/17 22:52:26  cheshire
+Get rid of unused mDNS_UpdateLLQs()
+
 Revision 1.128  2007/09/12 19:23:17  cheshire
 Get rid of unnecessary mDNSPlatformTCPIsConnected() routine
 
@@ -3672,10 +3675,6 @@ mDNSlocal void   ProcessingThreadInterfaceListChanged( mDNS *inMDNS )
        err = uDNS_SetupDNSConfig( inMDNS );
        check_noerr( err );
 
-       // so that LLQs are restarted against the up to date name servers
-
-       mDNS_UpdateLLQs( inMDNS );
-
        mDNSPlatformUnlock( inMDNS );
        
        // Inform clients of the change.
@@ -3737,10 +3736,6 @@ mDNSlocal void ProcessingThreadTCPIPConfigChanged( mDNS * inMDNS )
        err = uDNS_SetupDNSConfig( inMDNS );
        check_noerr( err );
 
-       // so that LLQs are restarted against the up to date name servers
-
-       mDNS_UpdateLLQs( inMDNS );
-
        // and reset the event handler
 
        if ( ( inMDNS->p->tcpipKey != NULL ) && ( inMDNS->p->tcpipChangedEvent ) )
@@ -3770,10 +3765,6 @@ mDNSlocal void   ProcessingThreadDynDNSConfigChanged( mDNS *inMDNS )
        err = uDNS_SetupDNSConfig( inMDNS );
        check_noerr( err );
 
-       // so that LLQs are restarted against the up to date name servers
-
-       mDNS_UpdateLLQs( inMDNS );
-
        // and reset the event handler
 
        if ((inMDNS->p->ddnsKey != NULL) && (inMDNS->p->ddnsChangedEvent))