From 96f69b28cfc861f450115f910b8a01ef5ba38806 Mon Sep 17 00:00:00 2001 From: Apple Date: Fri, 2 Nov 2007 22:13:42 +0000 Subject: [PATCH 1/1] mDNSResponder-164.tar.gz --- Clients/dns-sd.c | 29 +- Makefile | 2 +- mDNSCore/DNSCommon.c | 10 + mDNSCore/mDNS.c | 448 ++++++--- mDNSCore/mDNSEmbeddedAPI.h | 92 +- mDNSCore/uDNS.c | 1566 +++++++++++++------------------ mDNSCore/uDNS.h | 49 +- mDNSMacOSX/LegacyNATTraversal.c | 57 +- mDNSMacOSX/daemon.c | 45 +- mDNSMacOSX/mDNSMacOSX.c | 266 ++++-- mDNSMacOSX/mDNSMacOSX.h | 15 + mDNSPosix/Makefile | 36 +- mDNSPosix/PosixDaemon.c | 6 +- mDNSShared/PlatformCommon.c | 63 +- mDNSShared/PlatformCommon.h | 4 +- mDNSShared/dns_sd.h | 2 +- mDNSShared/dnsextd.c | 10 +- mDNSShared/uds_daemon.c | 209 +++-- mDNSWindows/mDNSWin32.c | 15 +- 19 files changed, 1566 insertions(+), 1358 deletions(-) diff --git a/Clients/dns-sd.c b/Clients/dns-sd.c index 279e5ef..718547c 100644 --- a/Clients/dns-sd.c +++ b/Clients/dns-sd.c @@ -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; diff --git a/Makefile b/Makefile index f19fe5a..d774618 100644 --- 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" diff --git a/mDNSCore/DNSCommon.c b/mDNSCore/DNSCommon.c index 2b4356d..01e063a 100644 --- a/mDNSCore/DNSCommon.c +++ b/mDNSCore/DNSCommon.c @@ -17,6 +17,10 @@ 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); diff --git a/mDNSCore/mDNS.c b/mDNSCore/mDNS.c index 4b0d972..854c656 100755 --- a/mDNSCore/mDNS.c +++ b/mDNSCore/mDNS.c @@ -38,6 +38,83 @@ Change History (most recent first): $Log: mDNS.c,v $ +Revision 1.751 2007/10/30 23:49:41 cheshire + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + BTMM: Machines don't appear in the sidebar on wake from sleep + +Revision 1.733 2007/10/17 22:37:23 cheshire + 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 + 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 + 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; iDupSuppress[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); diff --git a/mDNSCore/mDNSEmbeddedAPI.h b/mDNSCore/mDNSEmbeddedAPI.h index c761645..8673da9 100755 --- a/mDNSCore/mDNSEmbeddedAPI.h +++ b/mDNSCore/mDNSEmbeddedAPI.h @@ -54,6 +54,34 @@ 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 + 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 + BTMM: Machines don't appear in the sidebar on wake from sleep + +Revision 1.445 2007/10/17 22:37:23 cheshire + BTMM: Need to create NAT port mapping for receiving LLQ events + Revision 1.444 2007/09/29 03:14:52 cheshire 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]; diff --git a/mDNSCore/uDNS.c b/mDNSCore/uDNS.c index 9db4e9c..d6fddfa 100755 --- a/mDNSCore/uDNS.c +++ b/mDNSCore/uDNS.c @@ -22,11 +22,122 @@ Change History (most recent first): $Log: uDNS.c,v $ +Revision 1.528 2007/11/02 21:32:30 cheshire + 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 + 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 + BTMM: Need to retry registrations after failures + +Revision 1.523 2007/10/30 00:54:31 cheshire + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + BTMM: Need to retry registrations after failures + +Revision 1.508 2007/10/24 00:05:03 cheshire + 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 + 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 + 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 + 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 + BTMM: Machines don't appear in the sidebar on wake from sleep + +Revision 1.501 2007/10/17 22:37:23 cheshire + 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 + 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 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 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 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 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); } diff --git a/mDNSCore/uDNS.h b/mDNSCore/uDNS.h index bf7918d..c61e05c 100755 --- a/mDNSCore/uDNS.h +++ b/mDNSCore/uDNS.h @@ -17,6 +17,35 @@ 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 + 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 + BTMM: Machines don't appear in the sidebar on wake from sleep + +Revision 1.83 2007/10/17 22:37:23 cheshire + 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 diff --git a/mDNSMacOSX/LegacyNATTraversal.c b/mDNSMacOSX/LegacyNATTraversal.c index 09db9ca..2be3dd4 100644 --- a/mDNSMacOSX/LegacyNATTraversal.c +++ b/mDNSMacOSX/LegacyNATTraversal.c @@ -17,6 +17,19 @@ 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 + BTMM: Back to My Mac not working with D-Link DGL-4100 NAT gateway + +Revision 1.42 2007/10/16 17:37:18 cheshire + 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 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 "\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_ */ diff --git a/mDNSMacOSX/daemon.c b/mDNSMacOSX/daemon.c index 714c431..e7b7b9d 100644 --- a/mDNSMacOSX/daemon.c +++ b/mDNSMacOSX/daemon.c @@ -30,6 +30,16 @@ Change History (most recent first): $Log: daemon.c,v $ +Revision 1.347 2007/11/02 22:00:13 cheshire + BTMM: Work around keychain notification bug +Need to hold the lock while calling SetDomainSecrets + +Revision 1.346 2007/11/02 20:18:13 cheshire + BTMM: Work around keychain notification bug + +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 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 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; diff --git a/mDNSMacOSX/mDNSMacOSX.c b/mDNSMacOSX/mDNSMacOSX.c index bf316b5..5cba5ef 100644 --- a/mDNSMacOSX/mDNSMacOSX.c +++ b/mDNSMacOSX/mDNSMacOSX.c @@ -17,6 +17,47 @@ 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 + BTMM: Work around keychain notification bug + +Revision 1.506 2007/10/30 20:46:45 cheshire + BTMM: Need to retry registrations after failures + +Revision 1.505 2007/10/29 23:55:10 cheshire + 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 + 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 + 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 + 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 + 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 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 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" : ""); + // 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 diff --git a/mDNSMacOSX/mDNSMacOSX.h b/mDNSMacOSX/mDNSMacOSX.h index 6e8f12f..25fe6a4 100644 --- a/mDNSMacOSX/mDNSMacOSX.h +++ b/mDNSMacOSX/mDNSMacOSX.h @@ -17,6 +17,12 @@ Change History (most recent first): $Log: mDNSMacOSX.h,v $ +Revision 1.74 2007/11/02 20:18:13 cheshire + BTMM: Work around keychain notification bug + +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 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); diff --git a/mDNSPosix/Makefile b/mDNSPosix/Makefile index 91bc819..0a4763f 100755 --- a/mDNSPosix/Makefile +++ b/mDNSPosix/Makefile @@ -31,6 +31,13 @@ # 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 diff --git a/mDNSPosix/PosixDaemon.c b/mDNSPosix/PosixDaemon.c index 741ca59..be0523f 100644 --- a/mDNSPosix/PosixDaemon.c +++ b/mDNSPosix/PosixDaemon.c @@ -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 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); diff --git a/mDNSShared/PlatformCommon.c b/mDNSShared/PlatformCommon.c index 8ca7ec4..08b4945 100644 --- a/mDNSShared/PlatformCommon.c +++ b/mDNSShared/PlatformCommon.c @@ -17,6 +17,14 @@ 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 + 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 BTMM: Make AutoTunnel mode work with multihoming @@ -58,7 +66,7 @@ Move ReadDDNSSettingsFromConfFile() from mDNSMacOSX.c to PlatformCommon.c #include // Needed for fopen() etc. #include // Needed for close() #include // Needed for strlen() etc. -#include // Needed for errno etc. +#include // Needed for errno etc. #include // Needed for socket() etc. #include // 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); } diff --git a/mDNSShared/PlatformCommon.h b/mDNSShared/PlatformCommon.h index 4007ad3..44beb1c 100644 --- a/mDNSShared/PlatformCommon.h +++ b/mDNSShared/PlatformCommon.h @@ -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 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); diff --git a/mDNSShared/dns_sd.h b/mDNSShared/dns_sd.h index a3ee799..de55277 100644 --- a/mDNSShared/dns_sd.h +++ b/mDNSShared/dns_sd.h @@ -77,7 +77,7 @@ */ #ifndef _DNS_SD_H -#define _DNS_SD_H 1610100 +#define _DNS_SD_H 1640000 #ifdef __cplusplus extern "C" { diff --git a/mDNSShared/dnsextd.c b/mDNSShared/dnsextd.c index 38f1d3d..a3860f2 100644 --- a/mDNSShared/dnsextd.c +++ b/mDNSShared/dnsextd.c @@ -17,6 +17,12 @@ 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; diff --git a/mDNSShared/uds_daemon.c b/mDNSShared/uds_daemon.c index 604f39b..201a44b 100644 --- a/mDNSShared/uds_daemon.c +++ b/mDNSShared/uds_daemon.c @@ -17,6 +17,46 @@ 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 + 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 + Need "considerable burden on the network" warning in uds_daemon.c + +Revision 1.370 2007/10/17 18:44:23 cheshire + 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 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(""); + 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; diff --git a/mDNSWindows/mDNSWin32.c b/mDNSWindows/mDNSWin32.c index 1241a6f..13b0404 100755 --- a/mDNSWindows/mDNSWin32.c +++ b/mDNSWindows/mDNSWin32.c @@ -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)) -- 2.47.2