From: Apple Date: Tue, 15 Dec 2009 22:21:22 +0000 (+0000) Subject: mDNSResponder-214.3.tar.gz X-Git-Tag: mac-os-x-1063^0 X-Git-Url: https://git.saurik.com/apple/mdnsresponder.git/commitdiff_plain/ca3eca6bb9e566fa2f1db828ae1825ca099188fd?hp=1a1751620d06770d59106ce41cd9a1e82d48bc27 mDNSResponder-214.3.tar.gz --- diff --git a/Clients/PrinterSetupWizard/PrinterSetupWizard.vcproj b/Clients/PrinterSetupWizard/PrinterSetupWizard.vcproj index 65f5324..da14969 100644 --- a/Clients/PrinterSetupWizard/PrinterSetupWizard.vcproj +++ b/Clients/PrinterSetupWizard/PrinterSetupWizard.vcproj @@ -441,10 +441,6 @@ RelativePath="stdafx.cpp" > - - @@ -490,10 +486,6 @@ RelativePath="stdafx.h" > - - diff --git a/Clients/PrinterSetupWizard/ThirdPage.cpp b/Clients/PrinterSetupWizard/ThirdPage.cpp index 8981bd4..deca4df 100644 --- a/Clients/PrinterSetupWizard/ThirdPage.cpp +++ b/Clients/PrinterSetupWizard/ThirdPage.cpp @@ -162,7 +162,6 @@ First checked in #include "PrinterSetupWizardApp.h" #include "PrinterSetupWizardSheet.h" #include "ThirdPage.h" -#include "StdioFileEx.h" #include #include #include diff --git a/Clients/dns-sd.c b/Clients/dns-sd.c index 993632d..b945552 100644 --- a/Clients/dns-sd.c +++ b/Clients/dns-sd.c @@ -70,6 +70,14 @@ cl dns-sd.c -I../mDNSShared -DNOT_HAVE_GETOPT ws2_32.lib ..\mDNSWindows\DLL\Rele // aren't in the system's /usr/lib/libSystem.dylib. //#define TEST_NEW_CLIENTSTUB 1 +// When building mDNSResponder for Mac OS X 10.4 and earlier, /usr/lib/libSystem.dylib is built using its own private +// copy of dnssd_clientstub.c, which is old and doesn't have all the entry points defined in the latest version, so +// when we're building dns-sd.c on Mac OS X 10.4 or earlier, we automatically set TEST_NEW_CLIENTSTUB so that we'll +// embed a copy of the latest dnssd_clientstub.c instead of trying to link to the incomplete version in libSystem.dylib +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ <= 1040 +#define TEST_NEW_CLIENTSTUB 1 +#endif + #include #include // For stdout, stderr #include // For exit() diff --git a/Makefile b/Makefile index aaf7bc6..93d4c32 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ include /Developer/Makefiles/pb_makefiles/platform.make -MVERS = "mDNSResponder-214" +MVERS = "mDNSResponder-214.3" DDNSWRITECONFIG = "$(DSTROOT)/Library/Application Support/Bonjour/ddnswriteconfig" diff --git a/mDNSCore/DNSCommon.c b/mDNSCore/DNSCommon.c index bf57929..a866aa6 100644 --- a/mDNSCore/DNSCommon.c +++ b/mDNSCore/DNSCommon.c @@ -592,8 +592,9 @@ mDNSexport const mDNSEthAddr onesEthAddr = { { 255, 255, 255, 255, 255 mDNSexport const OwnerOptData zeroOwner = { 0, 0, { { 0 } }, { { 0 } }, { { 0 } } }; mDNSexport const mDNSInterfaceID mDNSInterface_Any = 0; -mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)1; -mDNSexport const mDNSInterfaceID mDNSInterface_Unicast = (mDNSInterfaceID)2; +mDNSexport const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)-1; +mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)-2; +mDNSexport const mDNSInterfaceID mDNSInterface_Unicast = (mDNSInterfaceID)-3; // Note: Microsoft's proposed "Link Local Multicast Name Resolution Protocol" (LLMNR) is essentially a limited version of // Multicast DNS, using the same packet formats, naming syntax, and record types as Multicast DNS, but on a different UDP diff --git a/mDNSCore/DNSCommon.h b/mDNSCore/DNSCommon.h index 95d4e30..b097680 100644 --- a/mDNSCore/DNSCommon.h +++ b/mDNSCore/DNSCommon.h @@ -17,6 +17,10 @@ Change History (most recent first): $Log: DNSCommon.h,v $ +svn merge: Revision 1.75 2009/07/21 23:35:01 cheshire + Sleep Proxy: Put owner OPT records in multicast announcements to avoid conflicts +Added PutRR_OS macros to put a ResourceRecord while taking into account the space needed to add an OWNER option at the end + Revision 1.73 2009/04/24 00:28:05 cheshire Return negative answers when host knows authoritatively that no answer exists Added definitions for RRTypeAnswersQuestionType/RRAssertsNonexistence/AnyTypeRecordAnswersQuestion @@ -326,20 +330,31 @@ extern mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 * // If we have a single large record to put in the packet, then we allow the packet to be up to 9K bytes, // but in the normal case we try to keep the packets below 1500 to avoid IP fragmentation on standard Ethernet + +#define AllowedRRSpace(msg) (((msg)->h.numAnswers || (msg)->h.numAuthorities || (msg)->h.numAdditionals) ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData) + extern mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl, const mDNSu8 *limit); #define PutResourceRecordTTL(msg, ptr, count, rr, ttl) \ - PutResourceRecordTTLWithLimit((msg), (ptr), (count), (rr), (ttl), \ - ((msg)->h.numAnswers || (msg)->h.numAuthorities || (msg)->h.numAdditionals) ? (msg)->data + NormalMaxDNSMessageData : (msg)->data + AbsoluteMaxDNSMessageData) + PutResourceRecordTTLWithLimit((msg), (ptr), (count), (rr), (ttl), (msg)->data + AllowedRRSpace(msg)) + #define PutResourceRecordTTLJumbo(msg, ptr, count, rr, ttl) \ PutResourceRecordTTLWithLimit((msg), (ptr), (count), (rr), (ttl), (msg)->data + AbsoluteMaxDNSMessageData) + #define PutResourceRecord(MSG, P, C, RR) PutResourceRecordTTL((MSG), (P), (C), (RR), (RR)->rroriginalttl) +// The PutRR_OS variants assume a local variable 'm', put build the packet at m->omsg, +// and assume a local variable 'OwnerRecordSpace' indicating how many bytes (if any) to reserve to add an OWNER option at the end +#define PutRR_OS_TTL(ptr, count, rr, ttl) \ + PutResourceRecordTTLWithLimit(&m->omsg, (ptr), (count), (rr), (ttl), m->omsg.data + AllowedRRSpace(&m->omsg) - OwnerRecordSpace) + +#define PutRR_OS(P, C, RR) PutRR_OS_TTL((P), (C), (RR), (RR)->rroriginalttl) + extern mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name, mDNSu16 rrtype, mDNSu16 rrclass); extern mDNSu8 *putZone(DNSMessage *const msg, mDNSu8 *ptr, mDNSu8 *limit, const domainname *zone, mDNSOpaque16 zoneClass); extern mDNSu8 *putPrereqNameNotInUse(const domainname *const name, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *end); extern mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr); -extern mDNSu8 *putDeleteRRSet(DNSMessage *msg, mDNSu8 *ptr, const domainname *name, mDNSu16 rrtype); +extern mDNSu8 *putDeleteRRSet(DNSMessage *msg, mDNSu8 *ptr, const domainname *name, mDNSu16 rrtype); extern mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domainname *name); extern mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease); diff --git a/mDNSCore/mDNS.c b/mDNSCore/mDNS.c index 8fe2a80..8bf8a32 100755 --- a/mDNSCore/mDNS.c +++ b/mDNSCore/mDNS.c @@ -38,6 +38,37 @@ Change History (most recent first): $Log: mDNS.c,v $ +Revision 1.977 2009/07/23 23:30:01 cheshire + Sleep Proxy: Ten-second maintenance wake not long enough to reliably get network connectivity + +Revision 1.976 2009/07/23 09:15:06 cheshire + Sleep Proxy: Put owner OPT records in multicast announcements to avoid conflicts +Fixed silly mistake in checkin 1.974 that broke SendResponses + +Revision 1.975 2009/07/21 23:46:19 cheshire +Improved "DNS Message too short" syslog debugging message + +Revision 1.974 2009/07/21 23:41:05 cheshire + Sleep Proxy: Put owner OPT records in multicast announcements to avoid conflicts +Fixed silly mistake in checkin 1.974 that broke SendResponses + +svn merge: Revision 1.974 2009/07/21 23:41:05 cheshire + Sleep Proxy: Put owner OPT records in multicast announcements to avoid conflicts +Another refinement: When building a response packet, if we're going to add an OWNER option at the end, +reserve enough bytes to ensure that we'll be able to do that + +svn merge: Revision 1.973 2009/07/16 00:34:18 cheshire + Sleep Proxy: Put owner OPT records in multicast announcements to avoid conflicts +Additional refinement: If we didn't register with a Sleep Proxy when going to sleep, +we don't need to include our OWNER option in our packets when we re-awaken + +svn merge: Revision 1.972 2009/07/16 00:12:23 cheshire + Sleep Proxy: Put owner OPT records in multicast announcements to avoid conflicts +Additional fixes: Only add and send OWNER option if we were already going to send a non-empty packet anyway + +svn merge: Revision 1.971 2009/07/15 23:35:40 cheshire + Sleep Proxy: Put owner OPT records in multicast announcements to avoid conflicts + Revision 1.970.2.1 2009/07/23 23:36:04 cheshire Sleep Proxy: Ten-second maintenance wake not long enough to reliably get network connectivity @@ -1522,7 +1553,6 @@ mDNSlocal void RetrySPSRegistrations(mDNS *const m); #define NO_HINFO 1 -mDNSlocal const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)~0; // Any records bigger than this are considered 'large' records #define SmallRecordLimit 1024 @@ -1624,10 +1654,10 @@ mDNSlocal NetworkInterfaceInfo *FirstInterfaceForID(mDNS *const m, const mDNSInt return(intf); } -mDNSlocal char *InterfaceNameForID(mDNS *const m, const mDNSInterfaceID InterfaceID) +mDNSexport char *InterfaceNameForID(mDNS *const m, const mDNSInterfaceID InterfaceID) { NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); - return(intf ? intf->ifname : ""); + return(intf ? intf->ifname : mDNSNULL); } // For a single given DNSQuestion, deliver an add/remove result for the single given AuthRecord @@ -2651,8 +2681,9 @@ mDNSlocal void SetupOwnerOpt(const mDNS *const m, const NetworkInterfaceInfo *co owner->u.owner.password = zeroEthAddr; // Don't try to compute the optlen until *after* we've set up the data fields + // Right now the DNSOpt_Owner_Space macro does not depend on the owner->u.owner being set up correctly, but in the future it might owner->opt = kDNSOpt_Owner; - owner->optlen = DNSOpt_Owner_Space(owner) - 4; + owner->optlen = DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC) - 4; } mDNSlocal void GrantUpdateCredit(AuthRecord *rr) @@ -2837,6 +2868,7 @@ mDNSlocal void SendResponses(mDNS *const m) while (intf) { + const int OwnerRecordSpace = (m->AnnounceOwner && intf->MAC.l[0]) ? DNSOpt_Header_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC) : 0; int numDereg = 0; int numAnnounce = 0; int numAnswer = 0; @@ -2855,7 +2887,7 @@ mDNSlocal void SendResponses(mDNS *const m) newptr = mDNSNULL; if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) { - newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0); + newptr = PutRR_OS_TTL(responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0); if (newptr) { responseptr = newptr; numDereg++; } } else if (rr->NewRData && !m->SleepState) // If we have new data for this record @@ -2865,14 +2897,14 @@ mDNSlocal void SendResponses(mDNS *const m) // See if we should send a courtesy "goodbye" for the old data before we replace it. if (ResourceRecordIsValidAnswer(rr) && rr->RequireGoodbye) { - newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0); + newptr = PutRR_OS_TTL(responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0); if (newptr) { responseptr = newptr; numDereg++; rr->RequireGoodbye = mDNSfalse; } } // Now try to see if we can fit the update in the same packet (not fatal if we can't) SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it - newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec); + newptr = PutRR_OS(responseptr, &m->omsg.h.numAnswers, &rr->resrec); rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state if (newptr) { responseptr = newptr; rr->RequireGoodbye = mDNStrue; } SetNewRData(&rr->resrec, OldRData, oldrdlength); @@ -2882,7 +2914,7 @@ mDNSlocal void SendResponses(mDNS *const m) mDNSu8 active = (m->SleepState != SleepState_Sleeping || intf->SPSAddr[0].type || intf->SPSAddr[1].type || intf->SPSAddr[2].type); if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it - newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, active ? rr->resrec.rroriginalttl : 0); + newptr = PutRR_OS_TTL(responseptr, &m->omsg.h.numAnswers, &rr->resrec, active ? rr->resrec.rroriginalttl : 0); rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state if (newptr) { @@ -2893,7 +2925,7 @@ mDNSlocal void SendResponses(mDNS *const m) // The first time through (pktcount==0), if this record is verified unique // (i.e. typically A, AAAA, SRV and TXT), set the flag to add an NSEC too. - if (!pktcount && active && rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->SendNSECNow) rr->SendNSECNow = (mDNSInterfaceID)1; + if (!pktcount && active && rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->SendNSECNow) rr->SendNSECNow = mDNSInterfaceMark; } if (newptr) // If succeeded in sending, advance to next interface @@ -2933,11 +2965,11 @@ mDNSlocal void SendResponses(mDNS *const m) { // The first time through (pktcount==0), if this record is verified unique // (i.e. typically A, AAAA, SRV and TXT), set the flag to add an NSEC too. - if (!pktcount && rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->SendNSECNow) rr->SendNSECNow = (mDNSInterfaceID)1; + if (!pktcount && rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->SendNSECNow) rr->SendNSECNow = mDNSInterfaceMark; if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it - newptr = PutResourceRecord(&m->omsg, newptr, &m->omsg.h.numAdditionals, &rr->resrec); + newptr = PutRR_OS(newptr, &m->omsg.h.numAdditionals, &rr->resrec); rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state if (newptr) { @@ -2956,7 +2988,7 @@ mDNSlocal void SendResponses(mDNS *const m) // Third Pass. Add NSEC records, if there's space. for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->SendNSECNow == (mDNSInterfaceID)1 || rr->SendNSECNow == intf->InterfaceID) + if (rr->SendNSECNow == mDNSInterfaceMark || rr->SendNSECNow == intf->InterfaceID) { AuthRecord nsec; mDNS_SetupResourceRecord(&nsec, mDNSNULL, mDNSInterface_Any, kDNSType_NSEC, rr->resrec.rroriginalttl, kDNSRecordTypeUnique, mDNSNULL, mDNSNULL); @@ -2972,25 +3004,41 @@ mDNSlocal void SendResponses(mDNS *const m) newptr = responseptr; if (!r2) // If we successfully built our NSEC record, add it to the packet now { - newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAdditionals, &nsec.resrec); + newptr = PutRR_OS(responseptr, &m->omsg.h.numAdditionals, &nsec.resrec); if (newptr) responseptr = newptr; } // If we successfully put the NSEC record, clear the SendNSECNow flag // If we consider this NSEC optional, then we unconditionally clear the SendNSECNow flag, even if we fail to put this additional record - if (newptr || rr->SendNSECNow == (mDNSInterfaceID)1) + if (newptr || rr->SendNSECNow == mDNSInterfaceMark) { rr->SendNSECNow = mDNSNULL; // Run through remainder of list clearing SendNSECNow flag for all other records which would generate the same NSEC for (r2 = rr->next; r2; r2=r2->next) if (SameResourceRecordNameClassInterface(r2, rr)) - if (r2->SendNSECNow == (mDNSInterfaceID)1 || r2->SendNSECNow == intf->InterfaceID) + if (r2->SendNSECNow == mDNSInterfaceMark || r2->SendNSECNow == intf->InterfaceID) r2->SendNSECNow = mDNSNULL; } } - if (m->omsg.h.numAnswers > 0 || m->omsg.h.numAdditionals) + if (m->omsg.h.numAnswers || m->omsg.h.numAdditionals) { + // If we have data to send, add OWNER option if necessary, then send packet + + if (OwnerRecordSpace) + { + AuthRecord opt; + mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL); + opt.resrec.rrclass = NormalMaxDNSMessageData; + opt.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record + opt.resrec.rdestimate = sizeof(rdataOPT); + SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]); + newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAdditionals, &opt.resrec); + if (newptr) { responseptr = newptr; LogSPS("SendResponses put %s", ARDisplayString(m, &opt)); } + else LogMsg("SendResponses: How did we fail to have space for the OPT record (%d/%d/%d/%d) %s", + m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); + } + debugf("SendResponses: Sending %d Deregistration%s, %d Announcement%s, %d Answer%s, %d Additional%s on %p", numDereg, numDereg == 1 ? "" : "s", numAnnounce, numAnnounce == 1 ? "" : "s", @@ -3140,19 +3188,12 @@ mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **quer mDNSBool ucast = (q->LargeAnswers || q->RequestUnicast) && m->CanReceiveUnicastOn5353; mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); const mDNSu8 *const limit = query->data + NormalMaxDNSMessageData; - mDNSu8 *newptr = putQuestion(query, *queryptr, limit, &q->qname, q->qtype, (mDNSu16)(q->qclass | ucbit)); + mDNSu8 *newptr = putQuestion(query, *queryptr, limit - *answerforecast, &q->qname, q->qtype, (mDNSu16)(q->qclass | ucbit)); if (!newptr) { debugf("BuildQuestion: No more space in this packet for question %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return(mDNSfalse); } - else if (newptr + *answerforecast >= limit) - { - verbosedebugf("BuildQuestion: Retracting question %##s (%s) new forecast total %d", - q->qname.c, DNSTypeName(q->qtype), newptr + *answerforecast - query->data); - query->h.numQuestions--; - return(mDNSfalse); - } else { mDNSu32 forecast = *answerforecast; @@ -3536,7 +3577,9 @@ mDNSlocal void SendQueries(mDNS *const m) { if (rr->AddressProxy.type == mDNSAddrType_IPv4) { - LogSPS("SendQueries ARP Probe %d %s %s", rr->ProbeCount, InterfaceNameForID(m, rr->resrec.InterfaceID), ARDisplayString(m,rr)); + char *ifname = InterfaceNameForID(m, rr->resrec.InterfaceID); + if (!ifname) ifname = ""; + LogSPS("SendQueries ARP Probe %d %s %s", rr->ProbeCount, ifname, ARDisplayString(m,rr)); SendARP(m, 1, rr, zerov4Addr.b, zeroEthAddr.b, rr->AddressProxy.ip.v4.b, rr->WakeUp.IMAC.b); } else if (rr->AddressProxy.type == mDNSAddrType_IPv6) @@ -3598,18 +3641,16 @@ mDNSlocal void SendQueries(mDNS *const m) // go through our interface list sending the appropriate queries on each interface while (intf) { - const int os = !intf->MAC.l[0] ? 0 : DNSOpt_Header_Space + mDNSSameEthAddress(&m->PrimaryMAC, &intf->MAC) ? DNSOpt_OwnerData_ID_Space : DNSOpt_OwnerData_ID_Wake_Space; - int OwnerRecordSpace = 0; + const int OwnerRecordSpace = (m->AnnounceOwner && intf->MAC.l[0]) ? DNSOpt_Header_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC) : 0; AuthRecord *rr; mDNSu8 *queryptr = m->omsg.data; - mDNSu8 *limit = m->omsg.data + AbsoluteMaxDNSMessageData; InitializeDNSMessage(&m->omsg.h, zeroID, QueryFlags); if (KnownAnswerList) verbosedebugf("SendQueries: KnownAnswerList set... Will continue from previous packet"); if (!KnownAnswerList) { // Start a new known-answer list CacheRecord **kalistptr = &KnownAnswerList; - mDNSu32 answerforecast = 0; + mDNSu32 answerforecast = OwnerRecordSpace; // We start by assuming we'll need at least enough space to put the Owner Option // Put query questions in this packet for (q = m->Questions; q && q != m->NewQuestions; q=q->next) @@ -3623,10 +3664,7 @@ mDNSlocal void SendQueries(mDNS *const m) // If we're suppressing this question, or we successfully put it, update its SendQNow state if (SuppressOnThisInterface(q->DupSuppress, intf) || BuildQuestion(m, &m->omsg, &queryptr, q, &kalistptr, &answerforecast)) - q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf); - - // Once we've put at least one question, cut back our limit to the normal single-packet size - if (m->omsg.h.numQuestions) limit = m->omsg.data + NormalMaxDNSMessageData; + q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf); } } @@ -3636,42 +3674,34 @@ mDNSlocal void SendQueries(mDNS *const m) { mDNSBool ucast = (rr->ProbeCount >= DefaultProbeCountForTypeUnique-1) && m->CanReceiveUnicastOn5353; mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); - mDNSu8 *newptr = putQuestion(&m->omsg, queryptr, limit, rr->resrec.name, kDNSQType_ANY, (mDNSu16)(rr->resrec.rrclass | ucbit)); + const mDNSu8 *const limit = m->omsg.data + (m->omsg.h.numQuestions ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData); // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) mDNSu32 forecast = answerforecast + 12 + rr->resrec.rdestimate; - if (newptr && newptr + forecast + os < limit) + mDNSu8 *newptr = putQuestion(&m->omsg, queryptr, limit - forecast, rr->resrec.name, kDNSQType_ANY, (mDNSu16)(rr->resrec.rrclass | ucbit)); + if (newptr) { - queryptr = newptr; - limit = m->omsg.data + NormalMaxDNSMessageData; - answerforecast = forecast; - OwnerRecordSpace = os; + queryptr = newptr; + answerforecast = forecast; rr->SendRNow = (rr->resrec.InterfaceID) ? mDNSNULL : GetNextActiveInterfaceID(intf); rr->IncludeInProbe = mDNStrue; verbosedebugf("SendQueries: Put Question %##s (%s) probecount %d", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->ProbeCount); } - else - { - verbosedebugf("SendQueries: Retracting Question %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); - m->omsg.h.numQuestions--; - } } } - if (m->omsg.h.numQuestions) limit = m->omsg.data + NormalMaxDNSMessageData - OwnerRecordSpace; - // Put our known answer list (either new one from this question or questions, or remainder of old one from last time) while (KnownAnswerList) { CacheRecord *ka = KnownAnswerList; mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - ka->TimeRcvd)) / mDNSPlatformOneSecond; - mDNSu8 *newptr = PutResourceRecordTTLWithLimit(&m->omsg, queryptr, &m->omsg.h.numAnswers, &ka->resrec, ka->resrec.rroriginalttl - SecsSinceRcvd, limit); + mDNSu8 *newptr = PutResourceRecordTTLWithLimit(&m->omsg, queryptr, &m->omsg.h.numAnswers, + &ka->resrec, ka->resrec.rroriginalttl - SecsSinceRcvd, m->omsg.data + NormalMaxDNSMessageData - OwnerRecordSpace); if (newptr) { verbosedebugf("SendQueries: Put %##s (%s) at %d - %d", ka->resrec.name->c, DNSTypeName(ka->resrec.rrtype), queryptr - m->omsg.data, newptr - m->omsg.data); queryptr = newptr; - limit = m->omsg.data + NormalMaxDNSMessageData - OwnerRecordSpace; KnownAnswerList = ka->NextInKAList; ka->NextInKAList = mDNSNULL; } @@ -3695,24 +3725,29 @@ mDNSlocal void SendQueries(mDNS *const m) else LogMsg("SendQueries: How did we fail to have space for the Update record %s", ARDisplayString(m,rr)); } - if (OwnerRecordSpace) - { - AuthRecord opt; - mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL); - opt.resrec.rrclass = NormalMaxDNSMessageData; - opt.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record - opt.resrec.rdestimate = sizeof(rdataOPT); - SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]); - LogSPS("SendQueries putting %s", ARDisplayString(m, &opt)); - queryptr = PutResourceRecordTTLWithLimit(&m->omsg, queryptr, &m->omsg.h.numAdditionals, - &opt.resrec, opt.resrec.rroriginalttl, m->omsg.data + AbsoluteMaxDNSMessageData); - if (!queryptr) - LogMsg("SendQueries: How did we fail to have space for the OPT record (%d/%d/%d/%d) %s", - m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); - } - if (queryptr > m->omsg.data) { + if (OwnerRecordSpace) + { + AuthRecord opt; + mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL); + opt.resrec.rrclass = NormalMaxDNSMessageData; + opt.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record + opt.resrec.rdestimate = sizeof(rdataOPT); + SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]); + LogSPS("SendQueries putting %s", ARDisplayString(m, &opt)); + queryptr = PutResourceRecordTTLWithLimit(&m->omsg, queryptr, &m->omsg.h.numAdditionals, + &opt.resrec, opt.resrec.rroriginalttl, m->omsg.data + AbsoluteMaxDNSMessageData); + if (!queryptr) + LogMsg("SendQueries: How did we fail to have space for the OPT record (%d/%d/%d/%d) %s", + m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); + if (queryptr > m->omsg.data + NormalMaxDNSMessageData) + if (m->omsg.h.numQuestions != 1 || m->omsg.h.numAnswers != 0 || m->omsg.h.numAuthorities != 1 || m->omsg.h.numAdditionals != 1) + LogMsg("SendQueries: Why did we generate oversized packet with OPT record %p %p %p (%d/%d/%d/%d) %s", + m->omsg.data, m->omsg.data + NormalMaxDNSMessageData, queryptr, + m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); + } + if ((m->omsg.h.flags.b[0] & kDNSFlag0_TC) && m->omsg.h.numQuestions > 1) LogMsg("SendQueries: Should not have more than one question (%d) in a truncated packet", m->omsg.h.numQuestions); debugf("SendQueries: Sending %d Question%s %d Answer%s %d Update%s on %p", @@ -4535,6 +4570,10 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) { int i; + // If there are DNS servers that will come out of the Penalty box, we should do that now + // so that any questions that we send below can start using that + ResetDNSServerPenalties(m); + verbosedebugf("mDNS_Execute"); if (m->CurrentQuestion) LogMsg("mDNS_Execute: ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype)); @@ -4571,6 +4610,9 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) SetSPSProxyListChanged(mDNSNULL); // Perform any deferred BPF reconfiguration now + // Clear AnnounceOwner if necessary. (Do this *before* SendQueries() and SendResponses().) + if (m->AnnounceOwner && m->timenow - m->AnnounceOwner >= 0) m->AnnounceOwner = 0; + if (m->DelaySleep && m->timenow - m->DelaySleep >= 0) { m->DelaySleep = 0; @@ -4764,8 +4806,7 @@ mDNSexport void mDNSCoreRestartQueries(mDNS *const m) mDNSlocal void SendSPSRegistration(mDNS *const m, NetworkInterfaceInfo *intf, const mDNSOpaque16 id) { - const int ownerspace = mDNSSameEthAddress(&m->PrimaryMAC, &intf->MAC) ? DNSOpt_OwnerData_ID_Space : DNSOpt_OwnerData_ID_Wake_Space; - const int optspace = DNSOpt_Header_Space + DNSOpt_LeaseData_Space + ownerspace; + const int optspace = DNSOpt_Header_Space + DNSOpt_LeaseData_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC); const int sps = intf->NextSPSAttempt / 3; AuthRecord *rr; @@ -4837,6 +4878,9 @@ mDNSlocal void SendSPSRegistration(mDNS *const m, NetworkInterfaceInfo *intf, co else { mStatus err; + // Once we've attempted to register, we need to include our OWNER option in our packets when we re-awaken + m->SentSleepProxyRegistration = mDNStrue; + LogSPS("SendSPSRegistration: Sending Update %s %d (%d) id %5d with %d records %d bytes to %#a:%d", intf->ifname, intf->NextSPSAttempt, sps, mDNSVal16(m->omsg.h.id), m->omsg.h.mDNS_numUpdates, p - m->omsg.data, &intf->SPSAddr[sps], mDNSVal16(intf->SPSPort[sps])); // if (intf->NextSPSAttempt < 5) m->omsg.h.flags = zeroID; // For simulating packet loss @@ -4987,7 +5031,8 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m) else { FindSPSInCache(m, &intf->NetWakeBrowse, sps); - if (!sps[0]) LogSPS("BeginSleepProcessing: %-6s %#a No Sleep Proxy Server found %d", intf->ifname, &intf->ip, intf->NetWakeBrowse.ThisQInterval); + if (!sps[0]) LogSPS("BeginSleepProcessing: %-6s %#a No Sleep Proxy Server found (Next Browse Q in %d, interval %d)", + intf->ifname, &intf->ip, intf->NetWakeBrowse.LastQTime + intf->NetWakeBrowse.ThisQInterval - m->timenow, intf->NetWakeBrowse.ThisQInterval); else { int i; @@ -5082,6 +5127,11 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) { m->SleepState = SleepState_Awake; m->SleepSeqNum++; + if (m->SentSleepProxyRegistration) // Include OWNER option in packets for 60 seconds after waking + { + m->SentSleepProxyRegistration = mDNSfalse; + m->AnnounceOwner = NonZeroTime(m->timenow + 60 * mDNSPlatformOneSecond); + } // If the machine wakes and then immediately tries to sleep again (e.g. a maintenance wake) // then we enforce a minimum delay of 16 seconds before we begin sleep processing. // This is to allow time for the Ethernet link to come up, DHCP to get an address, mDNS to issue queries, etc., @@ -5133,8 +5183,12 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) // 4. Refresh NAT mappings // We don't want to have to assume that all hardware can necessarily keep accurate // track of passage of time while asleep, so on wake we refresh our NAT mappings + // We typically wake up with no interfaces active, so there's no need to rush to try to find our external address. + // When we get a DHCP address and mDNS_SetPrimaryInterfaceInfo is called, we'll then set m->retryGetAddr + // to immediately request our external address from the NAT gateway. m->retryIntervalGetAddr = NATMAP_INIT_RETRY; - m->retryGetAddr = m->timenow; + m->retryGetAddr = m->timenow + mDNSPlatformOneSecond * 5; + LogInfo("mDNSCoreMachineSleep: retryGetAddr in %d %d", m->retryGetAddr - m->timenow, m->timenow); RecreateNATMappings(m); } @@ -6375,7 +6429,7 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, if (qptr) { LogInfo("Server %p responded with code %d to query %##s (%s)", qptr->qDNSServer, rcode, q.qname.c, DNSTypeName(q.qtype)); - PushDNSServerToEnd(m, qptr); + PenalizeDNSServer(m, qptr, mDNSfalse); } returnEarly = mDNStrue; } @@ -6584,8 +6638,37 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, } else if (m->rec.r.resrec.rroriginalttl > 0) { + DNSQuestion *q; //if (rr->resrec.rroriginalttl == 0) LogMsg("uDNS rescuing %s", CRDisplayString(m, rr)); RefreshCacheRecord(m, rr, m->rec.r.resrec.rroriginalttl); + + // We have to reset the question interval to MaxQuestionInterval so that we don't keep + // polling the network once we get a valid response back. For the first time when a new + // cache entry is created, AnswerCurrentQuestionWithResourceRecord does that. + // Subsequently, if we reissue questions from within the mDNSResponder e.g., DNS server + // configuration changed, without flushing the cache, we reset the question interval here. + // Currently, we do this for for both multicast and unicast questions as long as the record + // type is unique. For unicast, resource record is always unique and for multicast it is + // true for records like A etc. but not for PTR. + if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) + { + for (q = m->Questions; q; q=q->next) + { + + if (!q->DuplicateOf && !q->LongLived && + ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) + { + q->LastQTime = m->timenow; + q->LastQTxTime = m->timenow; + q->RecentAnswerPkts = 0; + q->ThisQInterval = MaxQuestionInterval; + q->RequestUnicast = mDNSfalse; + q->unansweredQueries = 0; + debugf("mDNSCoreReceiveResponse: Set MaxQuestionInterval for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + break; + } + } + } break; } else @@ -6849,8 +6932,9 @@ mDNSlocal void SPSRecordCallback(mDNS *const m, AuthRecord *const ar, mStatus re if (result == mStatus_NameConflict) { - LogMsg("Received Conflicting mDNS -- waking %s %.6a %s", - InterfaceNameForID(m, ar->resrec.InterfaceID), &ar->WakeUp.HMAC, ARDisplayString(m, ar)); + char *ifname = InterfaceNameForID(m, ar->resrec.InterfaceID); + if (!ifname) ifname = ""; + LogMsg("Received Conflicting mDNS -- waking %s %.6a %s", ifname, &ar->WakeUp.HMAC, ARDisplayString(m, ar)); SendWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.IMAC, &ar->WakeUp.password); } else if (result == mStatus_MemFree) @@ -6868,7 +6952,7 @@ mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m, int i; AuthRecord opt; mDNSu8 *p = m->omsg.data; - OwnerOptData owner; + OwnerOptData owner = zeroOwner; // Need to zero this, so we'll know if this Update packet was missing its Owner option mDNSu32 updatelease = 0; const mDNSu8 *ptr; @@ -7119,7 +7203,11 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *co #endif #endif - if ((unsigned)(end - (mDNSu8 *)pkt) < sizeof(DNSMessageHeader)) { LogMsg("DNS Message too short"); return; } + if ((unsigned)(end - (mDNSu8 *)pkt) < sizeof(DNSMessageHeader)) + { + LogMsg("DNS Message from %#a:%d to %#a:%d length %d too short", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt); + return; + } QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask); // Read the integer parts which are in IETF byte-order (MSB first, LSB second) ptr = (mDNSu8 *)&msg->h.numQuestions; @@ -7154,15 +7242,15 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *co else { LogMsg("Unknown DNS packet type %02X%02X from %#-15a:%-5d to %#-15a:%-5d length %d on %p (ignored)", - msg->h.flags.b[0], msg->h.flags.b[1], srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end-(mDNSu8 *)pkt, InterfaceID); + msg->h.flags.b[0], msg->h.flags.b[1], srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt, InterfaceID); if (mDNS_LoggingEnabled) { int i = 0; - while (ipenaltyTime != 0) + { + ptime = server->penaltyTime - m->timenow; + if (ptime < 0) + { + // This should always be a positive value between 0 and DNSSERVER_PENALTY_TIME + // If it does not get reset in ResetDNSServerPenalties for some reason, we do it + // here + LogMsg("PenaltyTimeForServer: PenaltyTime negative %d, (server penaltyTime %d, timenow %d) resetting the penalty", ptime, server->penaltyTime, m->timenow); + server->penaltyTime = 0; + ptime = 0; + } + } + return ptime; + } + +// Return the next server to "prev" if it is a match and unpenalized +mDNSlocal DNSServer *GetNextUnPenalizedServer(mDNS *m, DNSServer *prev) + { + int curmatchlen = -1; + DNSServer *curr = m->DNSServers; + + if (prev == mDNSNULL) return mDNSNULL; + + while (curr != mDNSNULL && curr != prev) + curr = curr->next; + + if (curr == mDNSNULL) + return mDNSNULL; + + + // We need to set the curmatchlen as though we are walking the list + // from the beginning. Otherwise, we may not pick the best match. + // For example, if we are looking up xxx.com, and we used the "xxx.com" + // entry the previous time and the next one is "com", we should not pick + // "com" now + curmatchlen = CountLabels(&curr->domain); + curr = curr->next; + while (curr != mDNSNULL) + { + int scount = CountLabels(&curr->domain); + + // Should not be delete because it is marked temporarily for cleaning up + // entries during configuration change and we pass NULL as the last argument + // to GetServerForName + if (curr->flags & DNSServer_FlagDelete) + { + LogInfo("GetServerForName: DNS Server is marked delete, cannot happen"); + curr = curr->next; + continue; + } + + + debugf("GetNextUnPenalizedServer: Address %#a (Domain %##s), PenaltyTime(abs) %d, PenaltyTime(rel) %d", &curr->addr, curr->domain.c, curr->penaltyTime, PenaltyTimeForServer(m,curr)); + + // Note the "==" in comparing scount and curmatchlen. When we picked a match + // for the question the first time, we already made sure that prev is the best match. + // Any other match is as good if we can find another entry with same number of + // labels. There can't be better matches that have more labels, because + // we would have picked that in the first place. Also we don't care what the + // name in the question is as we picked the best server for the question first + // time and the domain name is in prev now + + if ((curr->penaltyTime == 0) && (scount == curmatchlen) && SameDomainName(&prev->domain, &curr->domain)) + return curr; + curr = curr->next; + } + return mDNSNULL; + } + + +//Checks to see whether the newname is a better match for the name, given the best one we have +//seen so far (given in bestcount). +//Returns -1 if the newname is not a better match +//Returns 0 if the newname is the same as the old match +//Returns 1 if the newname is a better match +mDNSlocal int BetterMatchForName(const domainname *name, int namecount, const domainname *newname, int newcount, + int bestcount) + { + // If the name contains fewer labels than the new server's domain or the new name + // contains fewer labels than the current best, then it can't possibly be a better match + if (namecount < newcount || newcount < bestcount) return -1; + + // If there is no match, return -1 and the caller will skip this newname for + // selection + // + // If we find a match and the number of labels is the same as bestcount, then + // we return 0 so that the caller can do additional logic to pick one of + // the best based on some other factors e.g., penaltyTime + // + // If we find a match and the number of labels is more than bestcount, then we + // return 1 so that the caller can pick this over the old one. + // + // NOTE: newcount can either be equal or greater than bestcount beause of the + // check above. + + if (SameDomainName(SkipLeadingLabels(name, namecount - newcount), newname)) + return bestcount == newcount ? 0 : 1; + else + return -1; + } + +// Get the Best server that matches a name. If you find penalized servers, look for the one +// that will come out of the penalty box soon +mDNSlocal DNSServer *GetAnyBestServer(mDNS *m, const domainname *name) + { + DNSServer *curmatch = mDNSNULL; + int bestmatchlen = -1, namecount = name ? CountLabels(name) : 0; + DNSServer *curr; + mDNSs32 bestPenaltyTime; + int bettermatch; + + bestmatchlen = -1; + bestPenaltyTime = DNSSERVER_PENALTY_TIME + 1; + for (curr = m->DNSServers; curr; curr = curr->next) + { + int currcount = CountLabels(&curr->domain); + mDNSs32 currPenaltyTime = PenaltyTimeForServer(m, curr); + + debugf("GetAnyBestServer: Address %#a (Domain %##s), PenaltyTime(abs) %d, PenaltyTime(rel) %d", + &curr->addr, curr->domain.c, curr->penaltyTime, currPenaltyTime); + + + // If there are multiple best servers for a given question, we will pick the first one + // if none of them are penalized. If some of them are penalized in that list, we pick + // the least penalized one. BetterMatchForName walks through all best matches and + // "currPenaltyTime < bestPenaltyTime" check lets us either pick the first best server + // in the list when there are no penalized servers and least one among them + // when there are some penalized servers + + if (!(curr->flags & DNSServer_FlagDelete)) + { + + bettermatch = BetterMatchForName(name, namecount, &curr->domain, currcount, bestmatchlen); + + // If we found a better match (bettermatch == 1) then we don't need to + // compare penalty times. But if we found an equal match, then we compare + // the penalty times to pick a better match + + if ((bettermatch == 1) || ((bettermatch == 0) && currPenaltyTime < bestPenaltyTime)) + { curmatch = curr; bestmatchlen = currcount; bestPenaltyTime = currPenaltyTime;} + } + } + + return curmatch; + } + // Look up a DNS Server, matching by name in split-dns configurations. -mDNSexport DNSServer *GetServerForName(mDNS *m, const domainname *name) +mDNSexport DNSServer *GetServerForName(mDNS *m, const domainname *name, DNSServer *prev) { - DNSServer *curmatch = mDNSNULL, *p; - int curmatchlen = -1, ncount = name ? CountLabels(name) : 0; + DNSServer *curmatch = mDNSNULL; - for (p = m->DNSServers; p; p = p->next) + // prev is the previous DNS server used by some question + if (prev != mDNSNULL) { - int scount = CountLabels(&p->domain); - if (!(p->flags & DNSServer_FlagDelete) && ncount >= scount && scount > curmatchlen) - if (SameDomainName(SkipLeadingLabels(name, ncount - scount), &p->domain)) - { curmatch = p; curmatchlen = scount; } + curmatch = GetNextUnPenalizedServer(m, prev); + if (curmatch != mDNSNULL) + { + LogInfo("GetServerForName: Good DNS server %#a:%d (Penalty Time Left %d) found", &curmatch->addr, + mDNSVal16(curmatch->port), (curmatch->penaltyTime ? (curmatch->penaltyTime - m->timenow) : 0)); + return curmatch; + } } + + // We are here for many reasons. + // + // 1. We are looking up the DNS server first time for this question + // 2. We reached the end of list looking for unpenalized servers + // + // In the case of (1) we want to find the best match for the name. If nothing is penalized, + // we want the first one in the list. If some are penalized, we want the one that will get + // out of penalty box sooner + // + // In the case of (2) we want to select the first server that matches the name if StrictUnicastOrdering + // is TRUE. As penaltyTime is zero for all of them in that case, we automatically achieve that below. + // If StrictUnicastOrdering is FALSE, we want to pick the least penalized server in the list + + curmatch = GetAnyBestServer(m, name); + + if (curmatch != mDNSNULL) + LogInfo("GetServerForName: DNS server %#a:%d (Penalty Time Left %d) found", &curmatch->addr, + mDNSVal16(curmatch->port), (curmatch->penaltyTime ? (curmatch->penaltyTime - m->timenow) : 0)); + else + LogInfo("GetServerForName: no DNS server found"); + return(curmatch); } @@ -7444,7 +7707,7 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu // this routine with the question list data structures in an inconsistent state. if (!mDNSOpaque16IsZero(question->TargetQID)) { - question->qDNSServer = GetServerForName(m, &question->qname); + question->qDNSServer = GetServerForName(m, &question->qname, mDNSNULL); ActivateUnicastQuery(m, question, mDNSfalse); // If long-lived query, and we don't have our NAT mapping active, start it now @@ -9081,16 +9344,36 @@ mDNSexport mDNSOpaque16 mDNS_NewMessageID(mDNS * const m) #pragma mark - Sleep Proxy Server #endif -mDNSlocal void RestartProbing(mDNS *const m, AuthRecord *const rr) - { - // We reset ProbeCount, so we'll suppress our own answers for a while, to avoid generating ARP conflicts with a waking machine. - // If the machine does wake properly then we'll discard our records when we see the first new mDNS probe from that machine. - // If it does not wake (perhaps we just picked up a stray delayed packet sent before it went to sleep) - // then we'll transition out of probing state and start answering ARPs again. +mDNSlocal void RestartARPProbing(mDNS *const m, AuthRecord *const rr) + { + // If we see an ARP from a machine we think is sleeping, then either + // (i) the machine has woken, or + // (ii) it's just a stray old packet from before the machine slept + // To handle the second case, we reset ProbeCount, so we'll suppress our own answers for a while, to avoid + // generating ARP conflicts with a waking machine, and set rr->LastAPTime so we'll start probing again in 10 seconds. + // If the machine has just woken then we'll discard our records when we see the first new mDNS probe from that machine. + // If it was a stray old packet, then after 10 seconds we'll probe again and then start answering ARPs again. In this case we *do* + // need to send new ARP Announcements, because the owner's ARP broadcasts will have updated neighboring ARP caches, so we need to + // re-assert our (temporary) ownership of that IP address in order to receive subsequent packets addressed to that IPv4 address. + rr->resrec.RecordType = kDNSRecordTypeUnique; rr->ProbeCount = DefaultProbeCountForTypeUnique; - rr->AnnounceCount = InitialAnnounceCount; - InitializeLastAPTime(m, rr); + + // If we haven't started announcing yet (and we're not already in ten-second-delay mode) the machine is probably + // still going to sleep, so we just reset rr->ProbeCount so we'll continue probing until it stops responding. + // If we *have* started announcing, the machine is probably in the process of waking back up, so in that case + // we're more cautious and we wait ten seconds before probing it again. We do this because while waking from + // sleep, some network interfaces tend to lose or delay inbound packets, and without this delay, if the waking machine + // didn't answer our three probes within three seconds then we'd announce and cause it an unnecessary address conflict. + if (rr->AnnounceCount == InitialAnnounceCount && m->timenow - rr->LastAPTime >= 0) + InitializeLastAPTime(m, rr); + else + { + rr->AnnounceCount = InitialAnnounceCount; + rr->ThisAPInterval = mDNSPlatformOneSecond; + rr->LastAPTime = m->timenow + mDNSPlatformOneSecond * 9; // Send first packet at rr->LastAPTime + rr->ThisAPInterval, i.e. 10 seconds from now + SetNextAnnounceProbeTime(m, rr); + } } mDNSexport void mDNSCoreReceiveRawPacket(mDNS *const m, const mDNSu8 *const p, const mDNSu8 *const end, const mDNSInterfaceID InterfaceID) @@ -9115,13 +9398,18 @@ mDNSexport void mDNSCoreReceiveRawPacket(mDNS *const m, const mDNSu8 *const p, c // Pass 1: // Process ARP Requests and Probes (but not Announcements), and generate an ARP Reply if necessary. - // We also process and answer ARPs from our own kernel (no special treatment for localhost). + // We also process ARPs from our own kernel (and 'answer' them by injecting a local ARP table entry) // We ignore ARP Announcements here -- Announcements are not questions, they're assertions, so we don't need to answer them. - // The only time we might need to respond to an ARP Announcement is if it's a conflict -- and we check for that in Pass 2 below. + // The times we might need to react to an ARP Announcement are: + // (i) as an indication that the host in question has not gone to sleep yet (so we should delay beginning to proxy for it) or + // (ii) if it's a conflicting Announcement from another host + // -- and we check for these in Pass 2 below. if (mDNSSameOpaque16(arp->op, ARP_op_request) && !mDNSSameIPv4Address(arp->spa, arp->tpa)) for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->resrec.InterfaceID == InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->AddressProxy.ip.v4, arp->tpa)) { + char *ifname = InterfaceNameForID(m, InterfaceID); + if (!ifname) ifname = ""; static const char msg1[] = "ARP Req from owner -- re-probing"; static const char msg2[] = "Ignoring ARP Request from "; static const char msg3[] = "Creating Local ARP Cache entry "; @@ -9130,8 +9418,8 @@ mDNSexport void mDNSCoreReceiveRawPacket(mDNS *const m, const mDNSu8 *const p, c (rr->AnnounceCount == InitialAnnounceCount) ? msg2 : mDNSSameEthAddress(&arp->sha, &intf->MAC) ? msg3 : msg4; LogSPS("%-7s %s %.6a %.4a for %.4a -- H-MAC %.6a I-MAC %.6a %s", - InterfaceNameForID(m, InterfaceID), msg, &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); - if (msg == msg1) RestartProbing(m, rr); + ifname, msg, &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); + if (msg == msg1) RestartARPProbing(m, rr); else if (msg == msg3) mDNSPlatformSetLocalARP(&arp->tpa, &rr->WakeUp.IMAC, InterfaceID); else if (msg == msg4) SendARP(m, 2, rr, arp->tpa.b, arp->sha.b, arp->spa.b, arp->sha.b); } @@ -9152,16 +9440,19 @@ mDNSexport void mDNSCoreReceiveRawPacket(mDNS *const m, const mDNSu8 *const p, c for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->resrec.InterfaceID == InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->AddressProxy.ip.v4, arp->spa)) { - RestartProbing(m, rr); + char *ifname = InterfaceNameForID(m, InterfaceID); + if (!ifname) ifname = ""; + + RestartARPProbing(m, rr); if (mDNSSameEthAddress(&arp->sha, &rr->WakeUp.IMAC)) LogSPS("%-7s ARP %s from owner %.6a %.4a for %-15.4a -- re-starting probing for %s", - InterfaceNameForID(m, InterfaceID), + ifname, mDNSSameIPv4Address(arp->spa, arp->tpa) ? "Announcement" : mDNSSameOpaque16(arp->op, ARP_op_request) ? "Request " : "Response ", &arp->sha, &arp->spa, &arp->tpa, ARDisplayString(m, rr)); else { LogMsg("%-7s Conflicting ARP from %.6a %.4a for %.4a -- waking H-MAC %.6a I-MAC %.6a %s", - InterfaceNameForID(m, InterfaceID), &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); + ifname, &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); SendWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.IMAC, &rr->WakeUp.password); } } @@ -9178,10 +9469,8 @@ mDNSexport void mDNSCoreReceiveRawPacket(mDNS *const m, const mDNSu8 *const p, c { #define SSH_AsNumber 22 #define ARD_AsNumber 3283 - #define IPSEC_AsNumber 4500 static const mDNSIPPort SSH = { { SSH_AsNumber >> 8, SSH_AsNumber & 0xFF } }; static const mDNSIPPort ARD = { { ARD_AsNumber >> 8, ARD_AsNumber & 0xFF } }; - static const mDNSIPPort IPSEC = { { IPSEC_AsNumber >> 8, IPSEC_AsNumber & 0xFF } }; mDNSBool wake = mDNSfalse; mDNSIPPort port = zeroIPPort; @@ -9215,27 +9504,51 @@ mDNSexport void mDNSCoreReceiveRawPacket(mDNS *const m, const mDNSu8 *const p, c case 17: { const UDPHeader *const udp = (const UDPHeader *)trans; - mDNSu16 len = (mDNSu16)((mDNSu16)trans[4] << 8 | trans[5]); - port = udp->dst; - wake = mDNStrue; - - // For Back to My Mac UDP port 4500 (IPSEC) packets, we specially ignore NAT keepalive packets - if (mDNSSameIPPort(port, IPSEC)) wake = (len != 9 || end < trans + 9 || trans[8] != 0xFF); - - // For now, because we haven't yet worked out a clean elegant way to do this, we just special-case the - // Apple Remote Desktop port number -- we ignore all packets to UDP 3283 (the "Net Assistant" port), - // except for Apple Remote Desktop's explicit manual wakeup packet, which looks like this: - // UDP header (8 bytes) 13 88 00 6a 41 4e 41 20 (8 bytes) ffffffffffff (6 bytes) 16xMAC (96 bytes) = 118 bytes total - if (mDNSSameIPPort(port, ARD)) wake = (len >= 118 && end >= trans+10 && trans[8] == 0x13 && trans[9] == 0x88); - - LogSPS("%s %d-byte UDP from %.4a:%d to %.4a:%d", XX, &v4->src, mDNSVal16(udp->src), &v4->dst, mDNSVal16(port)); + const mDNSu16 udplen = (mDNSu16)((mDNSu16)trans[4] << 8 | trans[5]); // Length *including* 8-byte UDP header + if (udplen >= sizeof(UDPHeader)) + { + const mDNSu16 datalen = udplen - sizeof(UDPHeader); + port = udp->dst; + wake = mDNStrue; + + // For Back to My Mac UDP port 4500 (IPSEC) packets, we do some special handling + if (mDNSSameIPPort(port, IPSECPort)) + { + // Specifically ignore NAT keepalive packets + if (datalen == 1 && end >= trans + 9 && trans[8] == 0xFF) wake = mDNSfalse; + else + { + // Skip over the Non-ESP Marker if present + const mDNSBool NonESP = (end >= trans + 12 && trans[8] == 0 && trans[9] == 0 && trans[10] == 0 && trans[11] == 0); + const IKEHeader *const ike = (IKEHeader *)(trans + (NonESP ? 12 : 8)); + const mDNSu16 ikelen = datalen - (NonESP ? 4 : 0); + if (ikelen >= sizeof(IKEHeader) && end >= ((mDNSu8 *)ike) + sizeof(IKEHeader)) + if ((ike->Version & 0x10) == 0x10) + { + // ExchangeType == 5 means 'Informational' + // ExchangeType == 34 means 'IKE_SA_INIT' + if (ike->ExchangeType == 5 || ike->ExchangeType == 34) wake = mDNSfalse; + LogSPS("%s %d-byte IKE ExchangeType %d", XX, ike->ExchangeType); + } + } + } + + // For now, because we haven't yet worked out a clean elegant way to do this, we just special-case the + // Apple Remote Desktop port number -- we ignore all packets to UDP 3283 (the "Net Assistant" port), + // except for Apple Remote Desktop's explicit manual wakeup packet, which looks like this: + // UDP header (8 bytes) + // Payload: 13 88 00 6a 41 4e 41 20 (8 bytes) ffffffffffff (6 bytes) 16xMAC (96 bytes) = 110 bytes total + if (mDNSSameIPPort(port, ARD)) wake = (datalen >= 110 && end >= trans+10 && trans[8] == 0x13 && trans[9] == 0x88); + + LogSPS("%s %d-byte UDP from %.4a:%d to %.4a:%d", XX, &v4->src, mDNSVal16(udp->src), &v4->dst, mDNSVal16(port)); + } } break; default: LogSPS("%s %d-byte IP packet unknown protocol %d from %.4a to %.4a", XX, v4->protocol, &v4->src, &v4->dst); break; } - + if (wake) { AuthRecord *rr, *r2; @@ -9251,17 +9564,19 @@ mDNSexport void mDNSCoreReceiveRawPacket(mDNS *const m, const mDNSu8 *const p, c r2->resrec.rrtype == kDNSType_SRV && mDNSSameIPPort(r2->resrec.rdata->u.srv.port, port) && SameDomainLabel(ThirdLabel(r2->resrec.name)->c, tp)) break; - if (!r2 && mDNSSameIPPort(port, IPSEC)) r2 = rr; // So that we wake for BTMM IPSEC packets, even without a matching SRV record + if (!r2 && mDNSSameIPPort(port, IPSECPort)) r2 = rr; // So that we wake for BTMM IPSEC packets, even without a matching SRV record + char *ifname = InterfaceNameForID(m, rr->resrec.InterfaceID); + if (!ifname) ifname = ""; if (r2) { rr->AnnounceCount = 0; LogMsg("Waking host at %s %.4a H-MAC %.6a I-MAC %.6a for %s", - InterfaceNameForID(m, rr->resrec.InterfaceID), &v4->dst, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, r2)); + ifname, &v4->dst, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, r2)); SendWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.IMAC, &rr->WakeUp.password); } else LogSPS("Sleeping host at %s %.4a %.6a has no service on %#s %d", - InterfaceNameForID(m, rr->resrec.InterfaceID), &v4->dst, &rr->WakeUp.HMAC, tp, mDNSVal16(port)); + ifname, &v4->dst, &rr->WakeUp.HMAC, tp, mDNSVal16(port)); } mDNS_Unlock(m); } @@ -9415,6 +9730,8 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->SleepState = SleepState_Awake; m->SleepSeqNum = 0; m->SystemWakeOnLANEnabled = mDNSfalse; + m->SentSleepProxyRegistration = mDNSfalse; + m->AnnounceOwner = 0; m->DelaySleep = 0; m->SleepLimit = 0; @@ -9587,7 +9904,11 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) // Let the platform layer get the current DNS information // The m->RegisterSearchDomains boolean is so that we lazily get the search domain list only on-demand // (no need to hit the network with domain enumeration queries until we actually need that information). - for (ptr = m->DNSServers; ptr; ptr = ptr->next) ptr->flags |= DNSServer_FlagDelete; + for (ptr = m->DNSServers; ptr; ptr = ptr->next) + { + ptr->penaltyTime = 0; + ptr->flags |= DNSServer_FlagDelete; + } mDNSPlatformSetDNSConfig(m, mDNStrue, mDNSfalse, &fqdn, mDNSNULL, mDNSNULL); @@ -9595,7 +9916,7 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) for (q = m->Questions; q; q=q->next) if (!mDNSOpaque16IsZero(q->TargetQID)) { - DNSServer *s = GetServerForName(m, &q->qname); + DNSServer *s = GetServerForName(m, &q->qname, mDNSNULL); DNSServer *t = q->qDNSServer; if (t != s) { @@ -9606,6 +9927,10 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) q->qname.c, DNSTypeName(q->qtype)); q->qDNSServer = s; q->unansweredQueries = 0; + + // Change the query ID so that we won't cache responses to any in-flight queries + q->TargetQID = mDNS_NewMessageID(m); + ActivateUnicastQuery(m, q, mDNStrue); } } @@ -9613,8 +9938,9 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) // Flush all records that match a new resolver FORALL_CACHERECORDS(slot, cg, cr) { - ptr = GetServerForName(m, cr->resrec.name); - if (ptr && (ptr->flags & DNSServer_FlagNew) && !cr->resrec.InterfaceID) + if (cr->resrec.InterfaceID) continue; + ptr = GetServerForName(m, cr->resrec.name, mDNSNULL); + if (ptr && (ptr->flags & DNSServer_FlagNew)) PurgeOrReconfirmCacheRecord(m, cr, ptr, mDNSfalse); } @@ -9628,7 +9954,7 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) ptr = *p; ptr->flags &= ~DNSServer_FlagDelete; // Clear del so GetServerForName will (temporarily) find this server again before it's finally deleted FORALL_CACHERECORDS(slot, cg, cr) - if (!cr->resrec.InterfaceID && GetServerForName(m, cr->resrec.name) == ptr) + if (!cr->resrec.InterfaceID && GetServerForName(m, cr->resrec.name, mDNSNULL) == ptr) PurgeOrReconfirmCacheRecord(m, cr, ptr, mDNStrue); *p = (*p)->next; debugf("uDNS_SetupDNSConfig: Deleting server %p %#a:%d (%##s)", ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c); @@ -9651,12 +9977,11 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) FORALL_CACHERECORDS(slot, cg, cr) if (!cr->resrec.InterfaceID) { mDNS_PurgeCacheResourceRecord(m, cr); count++; } LogInfo("uDNS_SetupDNSConfig: %s available; purged %d unicast DNS records from cache", m->DNSServers ? "DNS server became" : "No DNS servers", count); + + // Force anything that needs to get zone data to get that information again + RestartRecordGetZoneData(m); } - // If we no longer have any DNS servers, we need to force anything that needs to get zone data - // to get that information again (which will fail, since we have no more DNS servers) - if ((m->DNSServers == mDNSNULL) && (oldServers != mDNSNULL)) RestartRecordGetZoneData(m); - // Did our FQDN change? if (!SameDomainName(&fqdn, &m->FQDN)) { diff --git a/mDNSCore/mDNSEmbeddedAPI.h b/mDNSCore/mDNSEmbeddedAPI.h index 00329ec..621d534 100755 --- a/mDNSCore/mDNSEmbeddedAPI.h +++ b/mDNSCore/mDNSEmbeddedAPI.h @@ -54,6 +54,14 @@ Change History (most recent first): $Log: mDNSEmbeddedAPI.h,v $ +Revision 1.577 2009/07/16 00:34:18 cheshire + Sleep Proxy: Put owner OPT records in multicast announcements to avoid conflicts +Additional refinement: If we didn't register with a Sleep Proxy when going to sleep, +we don't need to include our OWNER option in our packets when we re-awaken + +Revision 1.576 2009/07/15 23:35:37 cheshire + Sleep Proxy: Put owner OPT records in multicast announcements to avoid conflicts + Revision 1.575 2009/07/11 01:57:00 cheshire Sleep Proxy: Add support for using sleep proxy in local network interface hardware Added declaration of ActivateLocalProxy @@ -1466,6 +1474,18 @@ typedef packedstruct mDNSu16 checksum; } UDPHeader; // 8 bytes +typedef packedstruct + { + mDNSOpaque64 InitiatorCookie; + mDNSOpaque64 ResponderCookie; + mDNSu8 NextPayload; + mDNSu8 Version; + mDNSu8 ExchangeType; + mDNSu8 Flags; + mDNSOpaque32 MessageID; + mDNSu32 Length; + } IKEHeader; // 28 bytes + typedef packedstruct { mDNSIPPort src; @@ -1640,12 +1660,12 @@ typedef packedstruct ((O)->opt == kDNSOpt_Lease && (O)->optlen == DNSOpt_LeaseData_Space - 4) || \ ((O)->opt == kDNSOpt_Owner && ValidOwnerLength((O)->optlen) ) ) -#define DNSOpt_Owner_Space(O) (mDNSSameEthAddress(&(O)->u.owner.HMAC, &(O)->u.owner.IMAC) ? DNSOpt_OwnerData_ID_Space : DNSOpt_OwnerData_ID_Wake_Space) +#define DNSOpt_Owner_Space(A,B) (mDNSSameEthAddress((A),(B)) ? DNSOpt_OwnerData_ID_Space : DNSOpt_OwnerData_ID_Wake_Space) #define DNSOpt_Data_Space(O) ( \ (O)->opt == kDNSOpt_LLQ ? DNSOpt_LLQData_Space : \ (O)->opt == kDNSOpt_Lease ? DNSOpt_LeaseData_Space : \ - (O)->opt == kDNSOpt_Owner ? DNSOpt_Owner_Space(O) : 0x10000) + (O)->opt == kDNSOpt_Owner ? DNSOpt_Owner_Space(&(O)->u.owner.HMAC, &(O)->u.owner.IMAC) : 0x10000) // A maximal NSEC record is: // 256 bytes domainname 'nextname' @@ -2132,6 +2152,7 @@ typedef struct DNSServer mDNSu32 teststate; // Have we sent bug-detection query to this server? mDNSs32 lasttest; // Time we sent last bug-detection query to this server domainname domain; // name->server matching for "split dns" + mDNSs32 penaltyTime; // amount of time this server is penalized } DNSServer; typedef struct ExtraResourceRecord_struct ExtraResourceRecord; @@ -2332,7 +2353,10 @@ struct DNSQuestion_struct LLQ_State state; mDNSu32 ReqLease; // seconds (relative) mDNSs32 expire; // ticks (absolute) - mDNSs16 ntries; + mDNSs16 ntries; // for UDP: the number of packets sent for this LLQ state + // for TCP: there is some ambiguity in the use of this variable, but in general, it is + // the number of TCP/TLS connection attempts for this LLQ state, or + // the number of packets sent for this TCP/TLS connection mDNSOpaque64 id; // Client API fields: The client must set up these fields *before* calling mDNS_StartQuery() @@ -2573,6 +2597,8 @@ struct mDNS_struct mDNSu8 SleepState; // Set if we're sleeping mDNSu8 SleepSeqNum; // "Epoch number" of our current period of wakefulness mDNSu8 SystemWakeOnLANEnabled; // Set if we want to register with a Sleep Proxy before going to sleep + mDNSu8 SentSleepProxyRegistration;// Set if we registered (or tried to register) with a Sleep Proxy + mDNSs32 AnnounceOwner; // After waking from sleep, include OWNER option in packets until this time mDNSs32 DelaySleep; // To inhibit re-sleeping too quickly right after wake mDNSs32 SleepLimit; // Time window to allow deregistrations, etc., // during which underying platform layer should inhibit system sleep @@ -2710,6 +2736,7 @@ extern const OwnerOptData zeroOwner; extern const mDNSInterfaceID mDNSInterface_Any; // Zero extern const mDNSInterfaceID mDNSInterface_LocalOnly; // Special value extern const mDNSInterfaceID mDNSInterface_Unicast; // Special value +extern const mDNSInterfaceID mDNSInterfaceMark; // Special value extern const mDNSIPPort DiscardPort; extern const mDNSIPPort SSHPort; @@ -2739,6 +2766,8 @@ extern const mDNSOpaque16 UpdateRespFlags; extern const mDNSOpaque64 zeroOpaque64; +extern mDNSBool StrictUnicastOrdering; + #define localdomain (*(const domainname *)"\x5" "local") #define DeviceInfoName (*(const domainname *)"\xC" "_device-info" "\x4" "_tcp") #define SleepProxyServiceType (*(const domainname *)"\xC" "_sleep-proxy" "\x4" "_udp") @@ -2968,7 +2997,7 @@ extern mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, mDNS_DomainT extern mDNSOpaque16 mDNS_NewMessageID(mDNS *const m); -extern DNSServer *GetServerForName(mDNS *m, const domainname *name); +extern DNSServer *GetServerForName(mDNS *m, const domainname *name, DNSServer *current); // *************************************************************************** #if 0 @@ -3167,7 +3196,7 @@ extern void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCa 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 DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port); -extern void PushDNSServerToEnd(mDNS *const m, DNSQuestion *q); +extern void PenalizeDNSServer(mDNS *const m, DNSQuestion *q, mDNSBool QueryFail); extern void mDNS_AddSearchDomain(const domainname *const domain); // We use ((void *)0) here instead of mDNSNULL to avoid compile warnings on gcc 4.2 @@ -3418,6 +3447,7 @@ extern void FindSPSInCache(mDNS *const m, const DNSQuestion *const q, const Cach #define SPSMetric(X) (!ValidSPSName(X) || PrototypeSPSName(X) ? 1000000 : \ ((X)[1]-'0') * 100000 + ((X)[2]-'0') * 10000 + ((X)[4]-'0') * 1000 + ((X)[5]-'0') * 100 + ((X)[7]-'0') * 10 + ((X)[8]-'0')) extern void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheRecord *const rr, const QC_result AddRecord); +extern char *InterfaceNameForID(mDNS *const m, const mDNSInterfaceID InterfaceID); // 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 @@ -3466,7 +3496,8 @@ struct CompileTimeAssertionChecks_mDNS char assertI[(sizeof(IPv6Header ) == 40 ) ? 1 : -1]; char assertJ[(sizeof(IPv6ND ) == 24 ) ? 1 : -1]; char assertK[(sizeof(UDPHeader ) == 8 ) ? 1 : -1]; - char assertL[(sizeof(TCPHeader ) == 20 ) ? 1 : -1]; + char assertL[(sizeof(IKEHeader ) == 28 ) ? 1 : -1]; + char assertM[(sizeof(TCPHeader ) == 20 ) ? 1 : -1]; // Check our structures are reasonable sizes. Including overly-large buffers, or embedding // other overly-large structures instead of having a pointer to them, can inadvertently diff --git a/mDNSCore/uDNS.c b/mDNSCore/uDNS.c index 962c536..992e214 100755 --- a/mDNSCore/uDNS.c +++ b/mDNSCore/uDNS.c @@ -1345,6 +1345,9 @@ mDNSexport SearchListElem *SearchList = mDNSNULL; // would avoid the perils of modifying that list cleanly while some other piece of code is iterating through it. ServiceRecordSet *CurrentServiceRecordSet = mDNSNULL; +// The value can be set to true by the Platform code e.g., MacOSX uses the plist mechanism +mDNSBool StrictUnicastOrdering = mDNSfalse; + // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - General Utility Functions @@ -1486,44 +1489,110 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons (*p)->next = mDNSNULL; } } + (*p)->penaltyTime = 0; return(*p); } -mDNSexport void PushDNSServerToEnd(mDNS *const m, DNSQuestion *q) +// PenalizeDNSServer is called when the number of queries to the unicast +// DNS server exceeds MAX_UCAST_UNANSWERED_QUERIES or when we receive an +// error e.g., SERV_FAIL from DNS server. QueryFail is TRUE if this function +// is called when we exceed MAX_UCAST_UNANSWERED_QUERIES + +mDNSexport void PenalizeDNSServer(mDNS *const m, DNSQuestion *q, mDNSBool QueryFail) { DNSServer *orig = q->qDNSServer; - DNSServer **p = &m->DNSServers; if (m->mDNS_busy != m->mDNS_reentrancy+1) - LogMsg("PushDNSServerToEnd: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + LogMsg("PenalizeDNSServer: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); if (!q->qDNSServer) { - LogMsg("PushDNSServerToEnd: Null DNS server for %##s (%s) %d", q->qname.c, DNSTypeName(q->qtype), q->unansweredQueries); + LogMsg("PenalizeDNSServer: Null DNS server for %##s (%s) %d", q->qname.c, DNSTypeName(q->qtype), q->unansweredQueries); goto end; } - LogInfo("PushDNSServerToEnd: Pushing DNS server %#a:%d (%##s) due to %d unanswered queries for %##s (%s)", + if (QueryFail) + { + LogInfo("PenalizeDNSServer: DNS server %#a:%d (%##s) %d unanswered queries for %##s (%s)", &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qDNSServer->domain.c, q->unansweredQueries, q->qname.c, DNSTypeName(q->qtype)); - - while (*p) + } + else { - if (*p == q->qDNSServer) *p = q->qDNSServer->next; - else p=&(*p)->next; + LogInfo("PenalizeDNSServer: DNS server %#a:%d (%##s) Server Error for %##s (%s)", + &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qDNSServer->domain.c, q->qname.c, DNSTypeName(q->qtype)); } - *p = q->qDNSServer; - q->qDNSServer->next = mDNSNULL; + + // If strict ordering of unicast servers needs to be preserved, we just lookup + // the next best match server below + // + // If strict ordering is not required which is the default behavior, we penalize the server + // for DNSSERVER_PENALTY_TIME. We may also use additional logic e.g., don't penalize for PTR + // in the future. + + if (!StrictUnicastOrdering) + { + LogInfo("PenalizeDNSServer: Strict Unicast Ordering is FALSE"); + // We penalize the server so that new queries don't pick this server for DNSSERVER_PENALTY_TIME + // XXX Include other logic here to see if this server should really be penalized + // + if (q->qtype == kDNSType_PTR) + { + LogInfo("PenalizeDNSServer: Not Penalizing PTR question"); + } + else + { + LogInfo("PenalizeDNSServer: Penalizing question type %d", q->qtype); + q->qDNSServer->penaltyTime = NonZeroTime(m->timenow + DNSSERVER_PENALTY_TIME); + } + } + else + { + LogInfo("PenalizeDNSServer: Strict Unicast Ordering is TRUE"); + } end: - q->qDNSServer = GetServerForName(m, &q->qname); + q->qDNSServer = GetServerForName(m, &q->qname, q->qDNSServer); + + if ((q->qDNSServer != orig) && (QueryFail)) + { + // We picked a new server. In the case where QueryFail is true, the code has already incremented the interval + // and to compensate that we decrease it here. When two queries are sent, the QuestionIntervalStep is at 9. We just + // move it back to 3 here when we pick a new server. We can't start at 1 because if we have two servers failing, we will never + // backoff + // + q->ThisQInterval = q->ThisQInterval / QuestionIntervalStep; + if (q->qDNSServer) LogInfo("PenalizeDNSServer: Server for %##s (%s) changed to %#a:%d (%##s), Question Interval %u", q->qname.c, DNSTypeName(q->qtype), &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qDNSServer->domain.c, q->ThisQInterval); + else LogInfo("PenalizeDNSServer: Server for %##s (%s) changed to , Question Interval %u", q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval); - if (q->qDNSServer != orig) + } + else { - if (q->qDNSServer) LogInfo("PushDNSServerToEnd: Server for %##s (%s) changed to %#a:%d (%##s)", q->qname.c, DNSTypeName(q->qtype), &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qDNSServer->domain.c); - else LogInfo("PushDNSServerToEnd: Server for %##s (%s) changed to ", q->qname.c, DNSTypeName(q->qtype)); - q->ThisQInterval = q->ThisQInterval / QuestionIntervalStep; // Decrease interval one step so we don't quickly bounce between servers for queries that will not be answered. + // if we are here it means, + // + // 1) We picked the same server, QueryFail = false + // 2) We picked the same server, QueryFail = true + // 3) We picked a different server, QueryFail = false + // + // For all these three cases, ThisQInterval is already set properly + + if (q->qDNSServer) + { + if (q->qDNSServer != orig) + { + LogInfo("PenalizeDNSServer: Server for %##s (%s) changed to %#a:%d (%##s)", q->qname.c, DNSTypeName(q->qtype), &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qDNSServer->domain.c); + } + else + { + LogInfo("PenalizeDNSServer: Server for %##s (%s) remains the same at %#a:%d (%##s)", q->qname.c, DNSTypeName(q->qtype), &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qDNSServer->domain.c); + } + } + else + { + LogInfo("PenalizeDNSServer: Server for %##s (%s) changed to ", q->qname.c, DNSTypeName(q->qtype)); + } } + q->unansweredQueries = 0; } // *************************************************************************** @@ -2213,6 +2282,7 @@ mDNSlocal void tcpCallback(TCPSocket *sock, void *context, mDNSBool ConnectionEs // connection is established - send the message if (q && q->LongLived && q->state == LLQ_Established) { + // Lease renewal over TCP, resulting from opening a TCP connection in sendLLQRefresh end = ((mDNSu8*) &tcpInfo->request) + tcpInfo->requestLen; } else if (q && q->LongLived && q->state != LLQ_Poll && !mDNSIPPortIsZero(m->LLQNAT.ExternalPort) && !mDNSIPPortIsZero(q->servPort)) @@ -2232,9 +2302,11 @@ mDNSlocal void tcpCallback(TCPSocket *sock, void *context, mDNSBool ConnectionEs end = putLLQ(&tcpInfo->request, tcpInfo->request.data, q, &llqData); if (!end) { LogMsg("ERROR: tcpCallback - putLLQ"); err = mStatus_UnknownErr; goto exit; } AuthInfo = q->AuthInfo; // Need to add TSIG to this message + q->ntries = 0; // Reset ntries so that tcp/tls connection failures don't affect sendChallengeResponse failures } else if (q) { + // LLQ Polling mode or non-LLQ uDNS over TCP InitializeDNSMessage(&tcpInfo->request.h, q->TargetQID, uQueryFlags); end = putQuestion(&tcpInfo->request, tcpInfo->request.data, tcpInfo->request.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass); AuthInfo = q->AuthInfo; // Need to add TSIG to this message @@ -2261,16 +2333,33 @@ mDNSlocal void tcpCallback(TCPSocket *sock, void *context, mDNSBool ConnectionEs { mDNSu8 *lenptr = (mDNSu8 *)&tcpInfo->replylen; n = mDNSPlatformReadTCP(sock, lenptr + tcpInfo->nread, 2 - tcpInfo->nread, &closed); - if (n < 0) { LogMsg("ERROR: tcpCallback - attempt to read message length failed (%d)", n); err = mStatus_ConnFailed; goto exit; } + if (n < 0) + { + LogMsg("ERROR: tcpCallback - attempt to read message length failed (%d)", n); + err = mStatus_ConnFailed; + goto exit; + } else if (closed) { // It's perfectly fine for this socket to close after the first reply. The server might // be sending gratuitous replies using UDP and doesn't have a need to leave the TCP socket open. // We'll only log this event if we've never received a reply before. // BIND 9 appears to close an idle connection after 30 seconds. - if (tcpInfo->numReplies == 0) LogMsg("ERROR: socket closed prematurely tcpInfo->nread = %d", tcpInfo->nread); - err = mStatus_ConnFailed; - goto exit; + if (tcpInfo->numReplies == 0) + { + LogMsg("ERROR: socket closed prematurely tcpInfo->nread = %d", tcpInfo->nread); + err = mStatus_ConnFailed; + goto exit; + } + else + { + // Note that we may not be doing the best thing if an error occurs after we've sent a second request + // over this tcp connection. That is, we only track whether we've received at least one response + // which may have been to a previous request sent over this tcp connection. + if (backpointer) *backpointer = mDNSNULL; // Clear client backpointer FIRST so we don't risk double-disposing our tcpInfo_t + DisposeTCPConn(tcpInfo); + return; + } } tcpInfo->nread += n; @@ -2286,8 +2375,30 @@ mDNSlocal void tcpCallback(TCPSocket *sock, void *context, mDNSBool ConnectionEs n = mDNSPlatformReadTCP(sock, ((char *)tcpInfo->reply) + (tcpInfo->nread - 2), tcpInfo->replylen - (tcpInfo->nread - 2), &closed); - if (n < 0) { LogMsg("ERROR: tcpCallback - read returned %d", n); err = mStatus_ConnFailed; goto exit; } - else if (closed) { LogMsg("ERROR: socket closed prematurely %d", tcpInfo->nread); err = mStatus_ConnFailed; goto exit; } + if (n < 0) + { + LogMsg("ERROR: tcpCallback - read returned %d", n); + err = mStatus_ConnFailed; + goto exit; + } + else if (closed) + { + if (tcpInfo->numReplies == 0) + { + LogMsg("ERROR: socket closed prematurely tcpInfo->nread = %d", tcpInfo->nread); + err = mStatus_ConnFailed; + goto exit; + } + else + { + // Note that we may not be doing the best thing if an error occurs after we've sent a second request + // over this tcp connection. That is, we only track whether we've received at least one response + // which may have been to a previous request sent over this tcp connection. + if (backpointer) *backpointer = mDNSNULL; // Clear client backpointer FIRST so we don't risk double-disposing our tcpInfo_t + DisposeTCPConn(tcpInfo); + return; + } + } tcpInfo->nread += n; @@ -2338,14 +2449,58 @@ exit: if (q) { - if (q->ThisQInterval == 0 || q->LastQTime + q->ThisQInterval - m->timenow > MAX_UCAST_POLL_INTERVAL) + if (q->ThisQInterval == 0) + { + // We get here when we fail to establish a new TCP/TLS connection that would have been used for a new LLQ request or an LLQ renewal. + // Note that ThisQInterval is also zero when sendChallengeResponse resends the LLQ request on an extant TCP/TLS connection. + q->LastQTime = m->timenow; + if (q->LongLived) + { + // We didn't get the chance to send our request packet before the TCP/TLS connection failed. + // We want to retry quickly, but want to back off exponentially in case the server is having issues. + // Since ThisQInterval was 0, we can't just multiply by QuestionIntervalStep, we must track the number + // of TCP/TLS connection failures using ntries. + mDNSu32 count = q->ntries + 1; // want to wait at least 1 second before retrying + + q->ThisQInterval = InitialQuestionInterval; + + for (;count;count--) + q->ThisQInterval *= QuestionIntervalStep; + + if (q->ThisQInterval > LLQ_POLL_INTERVAL) + q->ThisQInterval = LLQ_POLL_INTERVAL; + else + q->ntries++; + + LogMsg("tcpCallback: stream connection for LLQ %##s (%s) failed %d times, retrying in %d ms", q->qname.c, DNSTypeName(q->qtype), q->ntries, q->ThisQInterval); + } + else + { + q->ThisQInterval = MAX_UCAST_POLL_INTERVAL; + LogMsg("tcpCallback: stream connection for %##s (%s) failed, retrying in %d ms", q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval); + } + SetNextQueryTime(m, q); + } + else if (q->LastQTime + q->ThisQInterval - m->timenow > (q->LongLived ? LLQ_POLL_INTERVAL : MAX_UCAST_POLL_INTERVAL)) { + // If we get an error and our next scheduled query for this question is more than the max interval from now, + // reset the next query to ensure we wait no longer the maximum interval from now before trying again. q->LastQTime = m->timenow; - q->ThisQInterval = MAX_UCAST_POLL_INTERVAL; + q->ThisQInterval = q->LongLived ? LLQ_POLL_INTERVAL : MAX_UCAST_POLL_INTERVAL; SetNextQueryTime(m, q); + LogMsg("tcpCallback: stream connection for %##s (%s) failed, retrying in %d ms", q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval); } - // ConnFailed may be actually okay. It just means that the server closed the connection but the LLQ may still be okay. - // If the error isn't ConnFailed, then the LLQ is in bad shape. + + // We're about to dispose of the TCP connection, so we must reset the state to retry over TCP/TLS + // because sendChallengeResponse will send the query via UDP if we don't have a tcp pointer. + // Resetting to LLQ_InitialRequest will cause uDNS_CheckCurrentQuestion to call startLLQHandshake, which + // will attempt to establish a new tcp connection. + if (q->LongLived && q->state == LLQ_SecondaryRequest) + q->state = LLQ_InitialRequest; + + // ConnFailed may happen if the server sends a TCP reset or TLS fails, in which case we want to retry establishing the LLQ + // quickly rather than switching to polling mode. This case is handled by the above code to set q->ThisQInterval just above. + // If the error isn't ConnFailed, then the LLQ is in bad shape, so we switch to polling mode. if (err != mStatus_ConnFailed) { if (q->LongLived && q->state != LLQ_Poll) StartLLQPolling(m, q); @@ -3451,14 +3606,21 @@ mDNSexport void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, co if (v4Changed || RouterChanged) { + // If we have a non-zero IPv4 address, we should try immediately to see if we have a NAT gateway + // If we have no IPv4 address, we don't want to be in quite such a hurry to report failures to our clients + // Sleeping server sometimes briefly disappears over Back to My Mac after it wakes up m->ExternalAddress = zerov4Addr; m->retryIntervalGetAddr = NATMAP_INIT_RETRY; - m->retryGetAddr = m->timenow; + m->retryGetAddr = m->timenow + (v4addr ? 0 : mDNSPlatformOneSecond * 5); m->NextScheduledNATOp = m->timenow; m->LastNATMapResultCode = NATErr_None; #ifdef _LEGACY_NAT_TRAVERSAL_ LNT_ClearState(m); #endif // _LEGACY_NAT_TRAVERSAL_ + LogInfo("mDNS_SetPrimaryInterfaceInfo:%s%s: retryGetAddr in %d %d", + v4Changed ? " v4Changed" : "", + RouterChanged ? " RouterChanged" : "", + m->retryGetAddr - m->timenow, m->timenow); } if (m->ReverseMap.ThisQInterval != -1) mDNS_StopQuery_internal(m, &m->ReverseMap); @@ -4707,8 +4869,7 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) DNSServer *orig = q->qDNSServer; if (orig) LogInfo("Sent %d unanswered queries for %##s (%s) to %#a:%d (%##s)", q->unansweredQueries, q->qname.c, DNSTypeName(q->qtype), &orig->addr, mDNSVal16(orig->port), orig->domain.c); - PushDNSServerToEnd(m, q); - q->unansweredQueries = 0; + PenalizeDNSServer(m, q, mDNStrue); } if (q->qDNSServer && q->qDNSServer->teststate != DNSServer_Disabled) @@ -4740,7 +4901,7 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) { 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; + if (q->state == LLQ_Poll) q->ThisQInterval = (LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10)) / QuestionIntervalStep; } else { @@ -4758,6 +4919,16 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) q->unansweredQueries++; if (q->ThisQInterval > MAX_UCAST_POLL_INTERVAL) q->ThisQInterval = MAX_UCAST_POLL_INTERVAL; + if (private && q->state != LLQ_Poll) + { + // We don't want to retransmit too soon. Hence, we always schedule our first + // retransmisson at 3 seconds rather than one second + if (q->ThisQInterval < (3 * mDNSPlatformOneSecond)) + q->ThisQInterval = q->ThisQInterval * QuestionIntervalStep; + if (q->ThisQInterval > LLQ_POLL_INTERVAL) + q->ThisQInterval = LLQ_POLL_INTERVAL; + LogInfo("uDNS_CheckCurrentQuestion: private non polling question for %##s (%s) will be retried in %d ms", q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval); + } debugf("Increased ThisQInterval to %d for %##s (%s)", q->ThisQInterval, q->qname.c, DNSTypeName(q->qtype)); } q->LastQTime = m->timenow; @@ -4781,7 +4952,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) debugf("uDNS_CheckCurrentQuestion no DNS server for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + if (!q->qDNSServer) LogInfo("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, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, mDNSInterface_Any); @@ -4840,6 +5011,8 @@ mDNSlocal void CheckNATMappings(mDNS *m) else if (m->retryIntervalGetAddr < NATMAP_MAX_RETRY_INTERVAL / 2) m->retryIntervalGetAddr *= 2; else m->retryIntervalGetAddr = NATMAP_MAX_RETRY_INTERVAL; } + LogInfo("CheckNATMappings retryGetAddr sent address request err %d interval %d", err, m->retryIntervalGetAddr); + // Always update m->retryGetAddr, even if we fail to send the packet. Otherwise in cases where we can't send the packet // (like when we have no active interfaces) we'll spin in an infinite loop repeatedly failing to send the packet m->retryGetAddr = m->timenow + m->retryIntervalGetAddr; @@ -4996,6 +5169,40 @@ mDNSlocal mDNSs32 CheckServiceRegistrations(mDNS *m) return nextevent; } +// This function is called early on in mDNS_Execute before any uDNS questions are +// dispatched so that if there are some good servers, the uDNS questions can now +// use it +mDNSexport void ResetDNSServerPenalties(mDNS *m) + { + DNSServer *d; + for (d = m->DNSServers; d; d=d->next) + { + if (d->penaltyTime != 0) + { + if (d->penaltyTime - m->timenow <= 0) + { + LogInfo("ResetDNSServerPenalties: DNS server %#a:%d out of penalty box", &d->addr, mDNSVal16(d->port)); + d->penaltyTime = 0; + } + } + } + } + +mDNSlocal mDNSs32 CheckDNSServerPenalties(mDNS *m) + { + mDNSs32 nextevent = m->timenow + 0x3FFFFFFF; + DNSServer *d; + for (d = m->DNSServers; d; d=d->next) + { + if (d->penaltyTime != 0) + { + if ((nextevent - d->penaltyTime) > 0) + nextevent = d->penaltyTime; + } + } + return nextevent; + } + mDNSexport void uDNS_Execute(mDNS *const m) { mDNSs32 nexte; @@ -5015,6 +5222,9 @@ mDNSexport void uDNS_Execute(mDNS *const m) nexte = CheckServiceRegistrations(m); if (nexte - m->NextuDNSEvent < 0) m->NextuDNSEvent = nexte; + + nexte = CheckDNSServerPenalties(m); + if (nexte - m->NextuDNSEvent < 0) m->NextuDNSEvent = nexte; } // *************************************************************************** diff --git a/mDNSCore/uDNS.h b/mDNSCore/uDNS.h index 71f4097..88beb9f 100755 --- a/mDNSCore/uDNS.h +++ b/mDNSCore/uDNS.h @@ -236,6 +236,7 @@ Revision 1.33 2006/07/05 22:53:28 cheshire #define LLQ_POLL_INTERVAL (15 * 60 * mDNSPlatformOneSecond) // Polling interval for zones w/ an advertised LLQ port (ie not static zones) if LLQ fails due to NAT, etc. #define RESPONSE_WINDOW (60 * mDNSPlatformOneSecond) // require server responses within one minute of request #define MAX_UCAST_UNANSWERED_QUERIES 2 // the number of unanswered queries from any one uDNS server before trying another server +#define DNSSERVER_PENALTY_TIME (60 * mDNSPlatformOneSecond) // number of seconds for which new questions don't pick this server #define DEFAULT_UPDATE_LEASE 7200 @@ -289,6 +290,7 @@ extern void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNSu8 * // returns time of next scheduled event extern void uDNS_Execute(mDNS *const m); +extern void ResetDNSServerPenalties(mDNS *m); extern mStatus uDNS_SetupDNSConfig(mDNS *const m); extern mStatus uDNS_RegisterSearchDomains(mDNS *const m); diff --git a/mDNSMacOSX/LaunchDaemonInfo-Tiger.helper.plist b/mDNSMacOSX/LaunchDaemonInfo-Tiger.helper.plist new file mode 100644 index 0000000..895287c --- /dev/null +++ b/mDNSMacOSX/LaunchDaemonInfo-Tiger.helper.plist @@ -0,0 +1,18 @@ + + + + + Label + com.apple.mDNSResponderHelper + OnDemand + + ProgramArguments + + /usr/sbin/mDNSResponderHelper + -t + 0 + + ServiceIPC + + + diff --git a/mDNSMacOSX/LaunchDaemonInfo-Tiger.plist b/mDNSMacOSX/LaunchDaemonInfo-Tiger.plist new file mode 100644 index 0000000..e91b775 --- /dev/null +++ b/mDNSMacOSX/LaunchDaemonInfo-Tiger.plist @@ -0,0 +1,17 @@ + + + + + Label + com.apple.mDNSResponder + OnDemand + + ProgramArguments + + /usr/sbin/mDNSResponder + -launchdaemon + + ServiceIPC + + + diff --git a/mDNSMacOSX/daemon.c b/mDNSMacOSX/daemon.c index 7e6db35..69e23d6 100644 --- a/mDNSMacOSX/daemon.c +++ b/mDNSMacOSX/daemon.c @@ -668,6 +668,8 @@ static int restarting_via_mach_init = 0; // Used on Jaguar/Panther when daemon i static int started_via_launchdaemon = 0; // Indicates we're running on Tiger or later, where daemon is managed by launchd static mDNSBool advertise = mDNS_Init_AdvertiseLocalAddresses; // By default, advertise addresses (& other records) via multicast +extern mDNSBool StrictUnicastOrdering; + //************************************************************************************************************* #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - @@ -1388,7 +1390,7 @@ mDNSlocal void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query) { kern_return_t status; DNSServiceResolver *x = (DNSServiceResolver *)query->ServiceInfoQueryContext; - NetworkInterfaceInfoOSX *ifx = (NetworkInterfaceInfoOSX *)query->info->InterfaceID; + NetworkInterfaceInfoOSX *ifx = IfindexToInterfaceInfoOSX(m, query->info->InterfaceID); if (query->info->InterfaceID == mDNSInterface_LocalOnly) ifx = mDNSNULL; struct sockaddr_storage interface; struct sockaddr_storage address; @@ -2346,9 +2348,10 @@ mDNSlocal void INFOCallback(void) { for (s = mDNSStorage.DNSServers; s; s = s->next) { - NetworkInterfaceInfoOSX *ifx = (NetworkInterfaceInfoOSX *)s->interface; - LogMsgNoIdent("DNS Server %##s %s%s%#a:%d %s", + NetworkInterfaceInfoOSX *ifx = IfindexToInterfaceInfoOSX(&mDNSStorage, s->interface); + LogMsgNoIdent("DNS Server %##s %s%s%#a:%d %d %s", s->domain.c, ifx ? ifx->ifinfo.ifname : "", ifx ? " " : "", &s->addr, mDNSVal16(s->port), + s->penaltyTime ? s->penaltyTime - mDNS_TimeNow(&mDNSStorage) : 0, s->teststate == DNSServer_Untested ? "(Untested)" : s->teststate == DNSServer_Passed ? "" : s->teststate == DNSServer_Failed ? "(Failed)" : @@ -3070,6 +3073,7 @@ mDNSexport int main(int argc, char **argv) if (!strcasecmp(argv[i], "-UnicastPacketLogging" )) mDNS_PacketLoggingEnabled = mDNStrue; if (!strcasecmp(argv[i], "-OfferSleepProxyService" )) OfferSleepProxyService = (i+1p->InterfaceList; i; i = i->next) + if (i->Registered && i->scope_id == scope_id) return(i); + + return mDNSNULL; +} + mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(mDNS *const m, mDNSu32 ifindex) { - NetworkInterfaceInfoOSX *i; if (ifindex == kDNSServiceInterfaceIndexLocalOnly) return(mDNSInterface_LocalOnly); if (ifindex == kDNSServiceInterfaceIndexAny ) return(mDNSNULL); - // Don't get tricked by inactive interfaces with no InterfaceID set - for (i = m->p->InterfaceList; i; i = i->next) - if (i->ifinfo.InterfaceID && i->scope_id == ifindex) return(i->ifinfo.InterfaceID); - - // Not found. Make sure our interface list is up to date, then try again. - LogInfo("InterfaceID for interface index %d not found; Updating interface list", ifindex); - mDNSMacOSXNetworkChanged(m); - for (i = m->p->InterfaceList; i; i = i->next) - if (i->ifinfo.InterfaceID && i->scope_id == ifindex) return(i->ifinfo.InterfaceID); + NetworkInterfaceInfoOSX* ifi = IfindexToInterfaceInfoOSX(m, (mDNSInterfaceID)(uintptr_t)ifindex); + if (!ifi) + { + // Not found. Make sure our interface list is up to date, then try again. + LogInfo("mDNSPlatformInterfaceIDfromInterfaceIndex: InterfaceID for interface index %d not found; Updating interface list", ifindex); + mDNSMacOSXNetworkChanged(m); + ifi = IfindexToInterfaceInfoOSX(m, (mDNSInterfaceID)(uintptr_t)ifindex); + } - return(mDNSNULL); + if (!ifi) return(mDNSNULL); + + return(ifi->ifinfo.InterfaceID); } + mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id) { NetworkInterfaceInfoOSX *i; if (id == mDNSInterface_LocalOnly) return(kDNSServiceInterfaceIndexLocalOnly); if (id == mDNSInterface_Any ) return(0); - // Don't use i->ifinfo.InterfaceID here, because we DO want to find inactive interfaces, which have no InterfaceID set + mDNSu32 scope_id = (mDNSu32)(uintptr_t)id; + + // Don't use i->Registered here, because we DO want to find inactive interfaces, which have no Registered set for (i = m->p->InterfaceList; i; i = i->next) - if ((mDNSInterfaceID)i == id) return(i->scope_id); + if (i->scope_id == scope_id) return(i->scope_id); // Not found. Make sure our interface list is up to date, then try again. LogInfo("Interface index for InterfaceID %p not found; Updating interface list", id); mDNSMacOSXNetworkChanged(m); for (i = m->p->InterfaceList; i; i = i->next) - if ((mDNSInterfaceID)i == id) return(i->scope_id); + if (i->scope_id == scope_id) return(i->scope_id); return(0); } @@ -1508,7 +1523,7 @@ mDNSexport void mDNSASLLog(uuid_t *uuid, const char *subdomain, const char *resu if (!asl_msg) { LogMsg("mDNSASLLog: asl_new failed"); return; } if (uuid) { - char uuidStr[36]; + char uuidStr[37]; uuid_unparse(*uuid, uuidStr); asl_set (asl_msg, "com.apple.message.uuid", uuidStr); } @@ -1560,15 +1575,23 @@ mDNSlocal mDNSBool AddrRequiresPPPConnection(const struct sockaddr *addr) mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, mDNSIPPort dstPort) { - // Note: For this platform we've adopted the convention that InterfaceIDs are secretly pointers - // to the NetworkInterfaceInfoOSX structure that holds the active sockets. The mDNSCore code - // doesn't know that and doesn't need to know that -- it just treats InterfaceIDs as opaque identifiers. - NetworkInterfaceInfoOSX *info = (NetworkInterfaceInfoOSX *)InterfaceID; - char *ifa_name = info ? info->ifinfo.ifname : "unicast"; + NetworkInterfaceInfoOSX *info = mDNSNULL; struct sockaddr_storage to; int s = -1, err; mStatus result = mStatus_NoError; + if (InterfaceID) + { + info = IfindexToInterfaceInfoOSX(m, InterfaceID); + if (info == NULL) + { + LogMsg("mDNSPlatformSendUDP: Invalid interface index %p", InterfaceID); + return mStatus_BadParamErr; + } + } + + char *ifa_name = InterfaceID ? info->ifinfo.ifname : "unicast"; + if (dst->type == mDNSAddrType_IPv4) { struct sockaddr_in *sin_to = (struct sockaddr_in*)&to; @@ -1809,14 +1832,18 @@ mDNSlocal void myKQSocketCallBack(int s1, short filter, void *context) // Note: When handling multiple packets in a batch, MUST reset InterfaceID before handling each packet mDNSInterfaceID InterfaceID = mDNSNULL; - NetworkInterfaceInfo *intf = m->HostInterfaces; - while (intf && strcmp(intf->ifname, packetifname)) intf = intf->next; + //NetworkInterfaceInfo *intf = m->HostInterfaces; + //while (intf && strcmp(intf->ifname, packetifname)) intf = intf->next; + + NetworkInterfaceInfoOSX *intf = m->p->InterfaceList; + while (intf && strcmp(intf->ifinfo.ifname, packetifname)) intf = intf->next; + // When going to sleep we deregister all our interfaces, but if the machine // takes a few seconds to sleep we may continue to receive multicasts // during that time, which would confuse mDNSCoreReceive, because as far // as it's concerned, we should have no active interfaces any more. // Hence we ignore multicasts for which we can find no matching InterfaceID. - if (intf) InterfaceID = intf->InterfaceID; + if (intf) InterfaceID = intf->ifinfo.InterfaceID; else if (mDNSAddrIsDNSMulticast(&destAddr)) continue; // LogMsg("myKQSocketCallBack got packet from %#a to %#a on interface %#a/%s", @@ -1963,7 +1990,7 @@ mDNSlocal void *doSSLHandshake(void *ctx) mStatus err = SSLHandshake(sock->tlsContext); KQueueLock(m); - LogInfo("doSSLHandshake %p: got lock", sock); // Log *after* we get the lock + debugf("doSSLHandshake %p: got lock", sock); // Log *after* we get the lock if (sock->handshake == handshake_to_be_closed) { @@ -1981,27 +2008,27 @@ mDNSlocal void *doSSLHandshake(void *ctx) { if (err) { - LogMsg("SSLHandshake failed: %d", err); + LogMsg("SSLHandshake failed: %d%s", err, err == errSSLPeerInternalError ? " (server busy)" : ""); SSLDisposeContext(sock->tlsContext); sock->tlsContext = NULL; } - sock->err = err; + sock->err = err ? mStatus_ConnFailed : 0; sock->handshake = handshake_completed; - LogInfo("doSSLHandshake: %p calling doTcpSocketCallback", sock); + debugf("doSSLHandshake: %p calling doTcpSocketCallback", sock); doTcpSocketCallback(sock); } } - LogInfo("SSLHandshake %p: dropping lock", sock); + debugf("SSLHandshake %p: dropping lock", sock); KQueueUnlock(m, "doSSLHandshake"); return NULL; } mDNSlocal mStatus spawnSSLHandshake(TCPSocket* sock) { - LogInfo("spawnSSLHandshake %p: entry", sock); + debugf("spawnSSLHandshake %p: entry", sock); if (sock->handshake != handshake_required) LogMsg("spawnSSLHandshake: handshake status not required: %d", sock->handshake); sock->handshake = handshake_in_progress; KQueueSet(sock->fd, EV_DELETE, EVFILT_READ, &sock->kqEntry); @@ -2017,7 +2044,7 @@ mDNSlocal mStatus spawnSSLHandshake(TCPSocket* sock) sock->err = err; KQueueSet(sock->fd, EV_ADD, EVFILT_READ, &sock->kqEntry); } - LogInfo("spawnSSLHandshake %p: done", sock); + debugf("spawnSSLHandshake %p: done", sock); return err; } @@ -2568,7 +2595,15 @@ mDNSexport void mDNSPlatformUDPClose(UDPSocket *sock) mDNSexport void mDNSPlatformSendRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID) { if (!InterfaceID) { LogMsg("mDNSPlatformSendRawPacket: No InterfaceID specified"); return; } - NetworkInterfaceInfoOSX *info = (NetworkInterfaceInfoOSX *)InterfaceID; + NetworkInterfaceInfoOSX *info; + + extern mDNS mDNSStorage; + info = IfindexToInterfaceInfoOSX(&mDNSStorage, InterfaceID); + if (info == NULL) + { + LogMsg("mDNSPlatformSendUDP: Invalid interface index %p", InterfaceID); + return; + } if (info->BPF_fd < 0) LogMsg("mDNSPlatformSendRawPacket: %s BPF_fd %d not ready", info->ifinfo.ifname, info->BPF_fd); else @@ -2582,7 +2617,14 @@ mDNSexport void mDNSPlatformSendRawPacket(const void *const msg, const mDNSu8 *c mDNSexport void mDNSPlatformSetLocalARP(const mDNSv4Addr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID) { if (!InterfaceID) { LogMsg("mDNSPlatformSetLocalARP: No InterfaceID specified"); return; } - NetworkInterfaceInfoOSX *info = (NetworkInterfaceInfoOSX *)InterfaceID; + NetworkInterfaceInfoOSX *info; + extern mDNS mDNSStorage; + info = IfindexToInterfaceInfoOSX(&mDNSStorage, InterfaceID); + if (info == NULL) + { + LogMsg("mDNSPlatformSendUDP: Invalid interface index %p", InterfaceID); + return; + } // Manually inject an entry into our local ARP cache. // (We can't do this by sending an ARP broadcast, because the kernel only pays attention to incoming ARP packets, not outgoing.) mDNSBool makearp = mDNSv4AddressIsLinkLocal(tpa); @@ -2663,16 +2705,16 @@ mDNSlocal int CountProxyTargets(mDNS *const m, NetworkInterfaceInfoOSX *x, int * AuthRecord *rr; for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.InterfaceID == (mDNSInterfaceID)x && rr->AddressProxy.type == mDNSAddrType_IPv4) + if (rr->resrec.InterfaceID == x->ifinfo.InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv4) { - if (p4) LogSPS("mDNSPlatformUpdateProxyList: fd %d %-7s IP%2d %.4a", x->BPF_fd, x->ifinfo.ifname, numv4, &rr->AddressProxy.ip.v4); + if (p4) LogSPS("CountProxyTargets: fd %d %-7s IP%2d %.4a", x->BPF_fd, x->ifinfo.ifname, numv4, &rr->AddressProxy.ip.v4); numv4++; } for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.InterfaceID == (mDNSInterfaceID)x && rr->AddressProxy.type == mDNSAddrType_IPv6) + if (rr->resrec.InterfaceID == x->ifinfo.InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv6) { - if (p6) LogSPS("mDNSPlatformUpdateProxyList: fd %d %-7s IP%2d %.16a", x->BPF_fd, x->ifinfo.ifname, numv6, &rr->AddressProxy.ip.v6); + if (p6) LogSPS("CountProxyTargets: fd %d %-7s IP%2d %.16a", x->BPF_fd, x->ifinfo.ifname, numv6, &rr->AddressProxy.ip.v6); numv6++; } @@ -2684,7 +2726,10 @@ mDNSlocal int CountProxyTargets(mDNS *const m, NetworkInterfaceInfoOSX *x, int * mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID InterfaceID) { NetworkInterfaceInfoOSX *x; - for (x = m->p->InterfaceList; x; x = x->next) if (x == (NetworkInterfaceInfoOSX *)InterfaceID) break; + + //NOTE: We can't use IfIndexToInterfaceInfoOSX because that looks for Registered also. + for (x = m->p->InterfaceList; x; x = x->next) if (x->ifinfo.InterfaceID == InterfaceID) break; + if (!x) { LogMsg("mDNSPlatformUpdateProxyList: ERROR InterfaceID %p not found", InterfaceID); return; } #define MAX_BPF_ADDRS 250 @@ -2730,9 +2775,9 @@ mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID static const struct bpf_insn g6 = BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 50); // Read IPv6 Dst LSW (bytes 50,51,52,53) static const struct bpf_insn r4a = BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14); // Get IP Header length (normally 20) - static const struct bpf_insn r4b = BPF_STMT(BPF_LD + BPF_IMM, 34); // A = 34 (14-byte Ethernet plus 20-byte TCP) + static const struct bpf_insn r4b = BPF_STMT(BPF_LD + BPF_IMM, 54); // A = 54 (14-byte Ethernet plus 20-byte TCP + 20 bytes spare) static const struct bpf_insn r4c = BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0); // A += IP Header length - static const struct bpf_insn r4d = BPF_STMT(BPF_RET + BPF_A, 0); // Success: Return Ethernet + IP + TCP + static const struct bpf_insn r4d = BPF_STMT(BPF_RET + BPF_A, 0); // Success: Return Ethernet + IP + TCP + 20 bytes spare (normally 74) static const struct bpf_insn r6a = BPF_STMT(BPF_RET + BPF_K, 94); // Success: Return Eth + IPv6 + TCP + 20 bytes spare @@ -2751,6 +2796,13 @@ mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID // In summary, if we byte-swap all the non-numeric fields that shouldn't be swapped, and we *don't* // swap any of the numeric values that *should* be byte-swapped, then the filter will work correctly. + // IPSEC capture size notes: + // 8 bytes UDP header + // 4 bytes Non-ESP Marker + // 28 bytes IKE Header + // -- + // 40 Total. Capturing TCP Header + 20 gets us enough bytes to receive the IKE Header in a UDP-encapsulated IKE packet. + AuthRecord *rr; for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->resrec.InterfaceID == InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv4) @@ -2857,7 +2909,7 @@ mDNSexport void mDNSPlatformReceiveBPF_fd(mDNS *const m, int fd) i->BPF_cfs = CFSocketCreateWithNative(kCFAllocatorDefault, fd, kCFSocketReadCallBack, bpf_callback, &myCFSocketContext); i->BPF_rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, i->BPF_cfs, 0); CFRunLoopAddSource(i->m->p->CFRunLoop, i->BPF_rls, kCFRunLoopDefaultMode); - mDNSPlatformUpdateProxyList(m, (mDNSInterfaceID)i); + mDNSPlatformUpdateProxyList(m, i->ifinfo.InterfaceID); } } @@ -3166,7 +3218,7 @@ mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifad // If this interface is not already registered (i.e. it's a dormant interface we had in our list // from when we previously saw it) then we mustn't do that, because mDNSCore doesn't know about it yet. // In this case, the mDNS_RegisterInterface() call will take care of starting the NetWake browse if necessary. - if ((*p)->ifinfo.InterfaceID) + if ((*p)->Registered) { mDNS_Lock(m); if (NetWake) mDNS_ActivateNetWake_internal (m, &(*p)->ifinfo); @@ -3182,7 +3234,7 @@ mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifad debugf("AddInterfaceToList: Making new interface %lu %.6a with address %#a at %p", scope_id, &bssid, &ip, i); if (!i) return(mDNSNULL); mDNSPlatformMemZero(i, sizeof(NetworkInterfaceInfoOSX)); - i->ifinfo.InterfaceID = mDNSNULL; + i->ifinfo.InterfaceID = (mDNSInterfaceID)(uintptr_t)scope_id; i->ifinfo.ip = ip; i->ifinfo.mask = mask; strlcpy(i->ifinfo.ifname, ifa->ifa_name, sizeof(i->ifinfo.ifname)); @@ -3205,6 +3257,7 @@ mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifad i->sa_family = ifa->ifa_addr->sa_family; i->BPF_fd = -1; i->BPF_len = 0; + i->Registered = mDNSNULL; // Do this AFTER i->BSSID has been set up i->ifinfo.NetWake = NetWakeInterface(i); @@ -3990,6 +4043,11 @@ mDNSlocal mStatus UpdateInterfaceList(mDNS *const m, mDNSs32 utc) LogMsg("getifaddrs ifa_netmask for %5s(%d) Flags %04X Family %2d %#a has different family: %d", ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family, &ip, ifa->ifa_netmask->sa_family); } + // Currently we use a few internal ones like mDNSInterfaceID_LocalOnly etc. that are negative values (0, -1, -2). + else if ((int)if_nametoindex(ifa->ifa_name) <= 0) + { + LogMsg("UpdateInterfaceList: if_nametoindex returned zero/negative value for %5s(%d)", ifa->ifa_name, if_nametoindex(ifa->ifa_name)); + } else { // Make sure ifa_netmask->sa_family is set correctly @@ -4171,18 +4229,20 @@ mDNSlocal int SetupActiveInterfaces(mDNS *const m, mDNSs32 utc) NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifinfo.ifname, AAAA_OVER_V4 ? AF_UNSPEC : i->sa_family); if (!primary) LogMsg("SetupActiveInterfaces ERROR! SearchForInterfaceByName didn't find %s", i->ifinfo.ifname); - if (n->InterfaceID && n->InterfaceID != (mDNSInterfaceID)primary) // Sanity check + if (i->Registered && i->Registered != primary) // Sanity check { - LogMsg("SetupActiveInterfaces ERROR! n->InterfaceID %p != primary %p", n->InterfaceID, primary); - n->InterfaceID = mDNSNULL; + LogMsg("SetupActiveInterfaces ERROR! n->Registered %p != primary %p", i->Registered, primary); + i->Registered = mDNSNULL; } - if (!n->InterfaceID) + if (!i->Registered) { - // Note: If n->InterfaceID is set, that means we've called mDNS_RegisterInterface() for this interface, + // Note: If i->Registered is set, that means we've called mDNS_RegisterInterface() for this interface, // so we need to make sure we call mDNS_DeregisterInterface() before disposing it. - // If n->InterfaceID is NOT set, then we haven't registered it and we should not try to deregister it - n->InterfaceID = (mDNSInterfaceID)primary; + // If i->Registered is NOT set, then we haven't registered it and we should not try to deregister it + // + + i->Registered = primary; // If i->LastSeen == utc, then this is a brand-new interface, just created, or an interface that never went away. // If i->LastSeen != utc, then this is an old interface, previously seen, that went away for (utc - i->LastSeen) seconds. @@ -4190,15 +4250,16 @@ mDNSlocal int SetupActiveInterfaces(mDNS *const m, mDNSs32 utc) i->Occulting = !(i->ifa_flags & IFF_LOOPBACK) && (utc - i->LastSeen > 0 && utc - i->LastSeen < 60); mDNS_RegisterInterface(m, n, i->Flashing && i->Occulting); + if (!mDNSAddressIsLinkLocal(&n->ip)) count++; - LogInfo("SetupActiveInterfaces: Registered %5s(%lu) %.6a InterfaceID %p %#a/%d%s%s%s", - i->ifinfo.ifname, i->scope_id, &i->BSSID, primary, &n->ip, CountMaskBits(&n->mask), + LogInfo("SetupActiveInterfaces: Registered %5s(%lu) %.6a InterfaceID %p(%p), primary %p, %#a/%d%s%s%s", + i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, i, primary, &n->ip, CountMaskBits(&n->mask), i->Flashing ? " (Flashing)" : "", i->Occulting ? " (Occulting)" : "", n->InterfaceActive ? " (Primary)" : ""); if (!n->McastTxRx) - debugf("SetupActiveInterfaces: No Tx/Rx on %5s(%lu) %.6a InterfaceID %p %#a", i->ifinfo.ifname, i->scope_id, &i->BSSID, primary, &n->ip); + debugf("SetupActiveInterfaces: No Tx/Rx on %5s(%lu) %.6a InterfaceID %p %#a", i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, &n->ip); else { if (i->sa_family == AF_INET) @@ -4285,22 +4346,22 @@ mDNSlocal int ClearInactiveInterfaces(mDNS *const m, mDNSs32 utc) { // If this interface is no longer active, or its InterfaceID is changing, deregister it NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifinfo.ifname, AAAA_OVER_V4 ? AF_UNSPEC : i->sa_family); - if (i->ifinfo.InterfaceID) - if (i->Exists == 0 || i->Exists == 2 || i->ifinfo.InterfaceID != (mDNSInterfaceID)primary) + if (i->Registered) + if (i->Exists == 0 || i->Exists == 2 || i->Registered != primary) { i->Flashing = !(i->ifa_flags & IFF_LOOPBACK) && (utc - i->AppearanceTime < 60); - LogInfo("ClearInactiveInterfaces: Deregistering %5s(%lu) %.6a InterfaceID %p %#a/%d%s%s%s", - i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, + LogInfo("ClearInactiveInterfaces: Deregistering %5s(%lu) %.6a InterfaceID %p(%p), primary %p, %#a/%d%s%s%s", + i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, i, primary, &i->ifinfo.ip, CountMaskBits(&i->ifinfo.mask), i->Flashing ? " (Flashing)" : "", i->Occulting ? " (Occulting)" : "", i->ifinfo.InterfaceActive ? " (Primary)" : ""); mDNS_DeregisterInterface(m, &i->ifinfo, i->Flashing && i->Occulting); if (!mDNSAddressIsLinkLocal(&i->ifinfo.ip)) count++; - i->ifinfo.InterfaceID = mDNSNULL; - // Note: If i->ifinfo.InterfaceID is set, that means we've called mDNS_RegisterInterface() for this interface, + i->Registered = mDNSNULL; + // Note: If i->Registered is set, that means we've called mDNS_RegisterInterface() for this interface, // so we need to make sure we call mDNS_DeregisterInterface() before disposing it. - // If i->ifinfo.InterfaceID is NOT set, then it's not registered and we should not call mDNS_DeregisterInterface() on it. + // If i->Registered is NOT set, then it's not registered and we should not call mDNS_DeregisterInterface() on it. // Caution: If we ever decide to add code here to leave the multicast group, we need to make sure that this // is the LAST representative of this physical interface, or we'll unsubscribe from the group prematurely. @@ -4317,9 +4378,9 @@ mDNSlocal int ClearInactiveInterfaces(mDNS *const m, mDNSs32 utc) if (!i->Exists) { if (i->LastSeen == utc) i->LastSeen = utc - 1; - mDNSBool delete = (NumCacheRecordsForInterfaceID(m, (mDNSInterfaceID)i) == 0) && (utc - i->LastSeen >= 60); - LogInfo("ClearInactiveInterfaces: %-13s %5s(%lu) %.6a InterfaceID %p %#a/%d Age %d%s", delete ? "Deleting" : "Holding", - i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, + mDNSBool delete = (NumCacheRecordsForInterfaceID(m, i->ifinfo.InterfaceID) == 0) && (utc - i->LastSeen >= 60); + LogInfo("ClearInactiveInterfaces: %-13s %5s(%lu) %.6a InterfaceID %p(%p) %#a/%d Age %d%s", delete ? "Deleting" : "Holding", + i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, i, &i->ifinfo.ip, CountMaskBits(&i->ifinfo.mask), utc - i->LastSeen, i->ifinfo.InterfaceActive ? " (Primary)" : ""); #if APPLE_OSX_mDNSResponder @@ -5626,7 +5687,7 @@ mDNSexport void mDNSMacOSXNetworkChanged(mDNS *const m) } else // else, we're Sleep Proxy Server; open BPF fds { - if (i->Exists && i->ifinfo.InterfaceID == (mDNSInterfaceID)i && !(i->ifa_flags & IFF_LOOPBACK) && i->BPF_fd == -1) + if (i->Exists && i->Registered == i && !(i->ifa_flags & IFF_LOOPBACK) && i->BPF_fd == -1) { LogSPS("%s requesting BPF", i->ifinfo.ifname); i->BPF_fd = -2; mDNSRequestBPF(); } } } diff --git a/mDNSMacOSX/mDNSMacOSX.h b/mDNSMacOSX/mDNSMacOSX.h index d2e0e73..03db235 100644 --- a/mDNSMacOSX/mDNSMacOSX.h +++ b/mDNSMacOSX/mDNSMacOSX.h @@ -255,6 +255,7 @@ struct NetworkInterfaceInfoOSX_struct u_int BPF_len; CFSocketRef BPF_cfs; CFRunLoopSourceRef BPF_rls; + NetworkInterfaceInfoOSX *Registered; // non-NULL means registered with mDNS Core }; struct mDNS_PlatformSupport_struct @@ -312,6 +313,7 @@ extern void NotifyOfElusiveBug(const char *title, const char *msg); // Both stri extern void SetDomainSecrets(mDNS *m); extern void mDNSMacOSXNetworkChanged(mDNS *const m); extern int mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring); +extern NetworkInterfaceInfoOSX *IfindexToInterfaceInfoOSX(const mDNS *const m, mDNSInterfaceID ifindex); extern int KQueueSet(int fd, u_short flags, short filter, const KQueueEntry *const entryRef); diff --git a/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj b/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj index cb3660f..bf25e00 100644 --- a/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj +++ b/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ isa = PBXAggregateTarget; buildConfigurationList = 03067D730C83A3CB0022BE1F /* Build configuration list for PBXAggregateTarget "Build Some" */; buildPhases = ( + FF045B6A0C7E4AA600448140 /* ShellScript */, ); dependencies = ( 03067D680C83A3830022BE1F /* PBXTargetDependency */, @@ -990,7 +991,6 @@ 2E0405ED0C31955500F13B59 /* Sources */, 2E0405EE0C31955500F13B59 /* Frameworks */, 4AAE0C5A0C68E6EC003882A5 /* CopyFiles */, - FF045B6A0C7E4AA600448140 /* ShellScript */, ); buildRules = ( ); @@ -1346,7 +1346,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if [ -e \"${SDKROOT}/usr/lib/libipsec.dylib\" ]\nthen\nrm -f \"${CONFIGURATION_TEMP_DIR}/ipsec_options.h\"\ntouch \"${CONFIGURATION_TEMP_DIR}/ipsec_options.h\"\nrm -f \"${CONFIGURATION_TEMP_DIR}/libipsec.a\"\nelse\necho \"#define MDNS_NO_IPSEC 1\" > ${CONFIGURATION_TEMP_DIR}/ipsec_options.h\ntouch \"${CONFIGURATION_TEMP_DIR}/empty.c\"\nfor i in ${ARCHS}\ndo\nccflags=\"-arch $i $ccflags\"\ndone\ncc ${ccflags} \"${CONFIGURATION_TEMP_DIR}/empty.c\" -c -o \"${CONFIGURATION_TEMP_DIR}/libipsec.a\"\nrm -f \"${CONFIGURATION_TEMP_DIR}/empty.c\"\nfi\n"; + shellScript = "if [ -e \"${SDKROOT}/usr/include/vproc.h\" ]\nthen\nrm -f \"${CONFIGURATION_TEMP_DIR}/vproc.h\"\nelse\ntouch \"${CONFIGURATION_TEMP_DIR}/vproc.h\"\nfi\n\nif [ -e \"${SDKROOT}/usr/lib/libipsec.dylib\" ]\nthen\nrm -f \"${CONFIGURATION_TEMP_DIR}/ipsec_options.h\"\ntouch \"${CONFIGURATION_TEMP_DIR}/ipsec_options.h\"\nrm -f \"${CONFIGURATION_TEMP_DIR}/libipsec.a\"\nelse\necho \"#define MDNS_NO_IPSEC 1\" > ${CONFIGURATION_TEMP_DIR}/ipsec_options.h\ntouch \"${CONFIGURATION_TEMP_DIR}/empty.c\"\nfor i in ${ARCHS}\ndo\nccflags=\"-arch $i $ccflags\"\ndone\ncc ${ccflags} \"${CONFIGURATION_TEMP_DIR}/empty.c\" -c -o \"${CONFIGURATION_TEMP_DIR}/libipsec.a\"\nrm -f \"${CONFIGURATION_TEMP_DIR}/empty.c\"\nfi\n"; }; D284BE510ADD80740027CCDF /* ShellScript */ = { isa = PBXShellScriptBuildPhase; @@ -1364,7 +1364,7 @@ ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/tcsh; - shellScript = "# Install plist to tell launchd to start mDNSResponder\nmkdir -p ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons\ncp ${SRCROOT}/LaunchDaemonInfo.plist ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponder.plist\n\n# Install mDNSResponder.bundle containing language localizations\nmkdir -p ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices\ncp -R ${SRCROOT}/mDNSResponder-bundle ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices/mDNSResponder.bundle\n\n# Remove unwanted CVS directories\nfind ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices/mDNSResponder.bundle -depth -name CVS -exec rm -rf {} \\;\n\n# Expand UTF-8 files to UTF-16 (at one time this appeared to be necessary, but it's not, so we don't do it any more)\n#foreach file (`find ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices/mDNSResponder.bundle -name Localizable.strings`)\n#iconv -f utf-8 -t utf-16 ${file} > ${file}.new\n#mv -f ${file}.new ${file}\n#end\n\n# Remove French localization (not wanted for Apple B&I builds)\nrm -rf ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices/mDNSResponder.bundle/Resources/French.lproj\n\n# Copy Sandbox profile, stripping initial license header to make the file fit in the ~16kB Sandbox profile limit\n(umask 022; mkdir -p -m 0755 ${DSTROOT}/usr/share/sandbox)\n(umask 222; awk '/^\\(version 1\\)$/,EOF' ${SRCROOT}/mDNSResponder.sb > ${DSTROOT}/usr/share/sandbox/mDNSResponder.sb)\n"; + shellScript = "# Install mDNSResponder.bundle containing language localizations\nmkdir -p ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices\ncp -R ${SRCROOT}/mDNSResponder-bundle ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices/mDNSResponder.bundle\n\n# Remove unwanted CVS directories\nfind ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices/mDNSResponder.bundle -depth -name CVS -exec rm -rf {} \\;\n\n# Expand UTF-8 files to UTF-16 (at one time this appeared to be necessary, but it's not, so we don't do it any more)\n#foreach file (`find ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices/mDNSResponder.bundle -name Localizable.strings`)\n#iconv -f utf-8 -t utf-16 ${file} > ${file}.new\n#mv -f ${file}.new ${file}\n#end\n\n# Remove French localization (not wanted for Apple B&I builds)\nrm -rf ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices/mDNSResponder.bundle/Resources/French.lproj\n\n# Copy Sandbox profile, stripping initial license header to make the file fit in the ~16kB Sandbox profile limit\n(umask 022; mkdir -p -m 0755 ${DSTROOT}/usr/share/sandbox)\n(umask 222; awk '/^\\(version 1\\)$/,EOF' ${SRCROOT}/mDNSResponder.sb > ${DSTROOT}/usr/share/sandbox/mDNSResponder.sb)\n"; }; D284BE760ADD80800027CCDF /* ShellScript */ = { isa = PBXShellScriptBuildPhase; @@ -1373,7 +1373,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if [ -e \"${SDKROOT}/usr/local/include/dnsinfo.h\" ]\nthen\nrm -f \"${CONFIGURATION_TEMP_DIR}/dnsinfo.h\"\nrm -f \"${CONFIGURATION_TEMP_DIR}/libdnsinfo.a\"\nelse\necho \"#define MDNS_NO_DNSINFO 1\" > ${CONFIGURATION_TEMP_DIR}/dnsinfo.h\ntouch \"${CONFIGURATION_TEMP_DIR}/empty.c\"\ncc -arch i386 -arch ppc \"${CONFIGURATION_TEMP_DIR}/empty.c\" -c -o \"${CONFIGURATION_TEMP_DIR}/libdnsinfo.a\"\nrm -f \"${CONFIGURATION_TEMP_DIR}/empty.c\"\nfi\n\nif [ -e \"${SDKROOT}/usr/include/sandbox.h\" ]\nthen\nrm -f \"${CONFIGURATION_TEMP_DIR}/sandbox.h\"\nelse\necho \"#define MDNS_NO_SANDBOX 1\" > \"${CONFIGURATION_TEMP_DIR}/sandbox.h\"\nfi\n"; + shellScript = "if [ -e \"${SDKROOT}/usr/local/include/dnsinfo.h\" ]\nthen\nrm -f \"${CONFIGURATION_TEMP_DIR}/dnsinfo.h\"\nrm -f \"${CONFIGURATION_TEMP_DIR}/libdnsinfo.a\"\nelse\necho \"#define MDNS_NO_DNSINFO 1\" > ${CONFIGURATION_TEMP_DIR}/dnsinfo.h\ntouch \"${CONFIGURATION_TEMP_DIR}/empty.c\"\nfor i in ${ARCHS}\ndo\nccflags=\"-arch $i $ccflags\"\ndone\ncc ${ccflags} \"${CONFIGURATION_TEMP_DIR}/empty.c\" -c -o \"${CONFIGURATION_TEMP_DIR}/libdnsinfo.a\"\nrm -f \"${CONFIGURATION_TEMP_DIR}/empty.c\"\nfi\n\nif [ -e \"${SDKROOT}/usr/include/sandbox.h\" ]\nthen\nrm -f \"${CONFIGURATION_TEMP_DIR}/sandbox.h\"\nelse\necho \"#define MDNS_NO_SANDBOX 1\" > \"${CONFIGURATION_TEMP_DIR}/sandbox.h\"\nfi\n\nif [ -e \"${SDKROOT}/usr/include/vproc.h\" ]\nthen\nrm -f \"${CONFIGURATION_TEMP_DIR}/vproc.h\"\nelse\ntouch \"${CONFIGURATION_TEMP_DIR}/vproc.h\"\nfi\n\nif [ -e \"${SDKROOT}/System/Library/Frameworks/IOKit.framework/PrivateHeaders/pwr_mgt/IOPMLibPrivate.h\" ]\nthen\nrm -rf \"${CONFIGURATION_TEMP_DIR}/IOKit\"\nelse\nmkdir -p \"${CONFIGURATION_TEMP_DIR}/IOKit/pwr_mgt\"\ntouch \"${CONFIGURATION_TEMP_DIR}/IOKit/pwr_mgt/IOPMLibPrivate.h\"\nfi\n"; }; FF045B6A0C7E4AA600448140 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; @@ -1386,7 +1386,7 @@ ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/tcsh; - shellScript = "# Install plist to tell launchd how to start mDNSResponderHelper\nmkdir -p ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons\ncp ${SRCROOT}/LaunchDaemonInfo.helper.plist ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponderHelper.plist\n"; + shellScript = "# Install plists to tell launchd how to start mDNSResponder and mDNSResponderHelper\nmkdir -p ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons\n\nif (${MACOSX_DEPLOYMENT_TARGET} == \"10.4\") then\ncp ${SRCROOT}/LaunchDaemonInfo-Tiger.plist ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponder.plist\ncp ${SRCROOT}/LaunchDaemonInfo-Tiger.helper.plist ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponderHelper.plist\nelse\ncp ${SRCROOT}/LaunchDaemonInfo.plist ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponder.plist\ncp ${SRCROOT}/LaunchDaemonInfo.helper.plist ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponderHelper.plist\nendif\n"; }; FF37FAAD0BC581780044A5CF /* ShellScript */ = { isa = PBXShellScriptBuildPhase; @@ -1856,7 +1856,6 @@ GCC_GENERATE_DEBUGGING_SYMBOLS = YES; HEADER_SEARCH_PATHS = ../mDNSShared; INSTALL_PATH = /usr/bin; - MACOSX_DEPLOYMENT_TARGET = 10.4; OTHER_CFLAGS = "-no-cpp-precomp"; OTHER_LDFLAGS = ""; OTHER_REZFLAGS = ""; diff --git a/mDNSShared/dns_sd.h b/mDNSShared/dns_sd.h index 59891f7..39d84a6 100644 --- a/mDNSShared/dns_sd.h +++ b/mDNSShared/dns_sd.h @@ -77,7 +77,7 @@ */ #ifndef _DNS_SD_H -#define _DNS_SD_H 2140000 +#define _DNS_SD_H 2140300 #ifdef __cplusplus extern "C" { diff --git a/mDNSShared/uds_daemon.c b/mDNSShared/uds_daemon.c index 9022f94..8b89b15 100644 --- a/mDNSShared/uds_daemon.c +++ b/mDNSShared/uds_daemon.c @@ -4138,7 +4138,7 @@ mDNSlocal void LogAuthRecords(mDNS *const m, const mDNSs32 now, AuthRecord *Reso LogMsgNoIdent(" Int Next Expire State"); for (ar = ResourceRecords; ar; ar=ar->next) { - NetworkInterfaceInfo *info = (NetworkInterfaceInfo *)ar->resrec.InterfaceID; + char *ifname = InterfaceNameForID(m, ar->resrec.InterfaceID); if (ar->WakeUp.HMAC.l[0]) (*proxy)++; if (!mDNSSameEthAddress(&owner, &ar->WakeUp.HMAC)) { @@ -4161,7 +4161,7 @@ mDNSlocal void LogAuthRecords(mDNS *const m, const mDNSs32 now, AuthRecord *Reso ar->ThisAPInterval / mDNSPlatformOneSecond, ar->AnnounceCount ? (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond : 0, ar->TimeExpire ? (ar->TimeExpire - now) / mDNSPlatformOneSecond : 0, - info ? info->ifname : "ALL", + ifname ? ifname : "ALL", ARDisplayString(m, ar)); else LogMsgNoIdent(" LO %s", ARDisplayString(m, ar)); @@ -4191,14 +4191,14 @@ mDNSexport void udsserver_info(mDNS *const m) for (cr = cg->members; cr; cr=cr->next) { mDNSs32 remain = cr->resrec.rroriginalttl - (now - cr->TimeRcvd) / mDNSPlatformOneSecond; - NetworkInterfaceInfo *info = (NetworkInterfaceInfo *)cr->resrec.InterfaceID; + char *ifname = InterfaceNameForID(m, cr->resrec.InterfaceID); CacheUsed++; if (cr->CRActiveQuestion) CacheActive++; LogMsgNoIdent("%3d %s%8ld %-7s%s %-6s%s", slot, cr->CRActiveQuestion ? "*" : " ", remain, - info ? info->ifname : "-U-", + ifname ? ifname : "-U-", (cr->resrec.RecordType == kDNSRecordTypePacketNegative) ? "-" : (cr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? " " : "+", DNSTypeName(cr->resrec.rrtype), @@ -4244,12 +4244,12 @@ mDNSexport void udsserver_info(mDNS *const m) { mDNSs32 i = q->ThisQInterval / mDNSPlatformOneSecond; mDNSs32 n = (q->LastQTime + q->ThisQInterval - now) / mDNSPlatformOneSecond; - NetworkInterfaceInfo *info = (NetworkInterfaceInfo *)q->InterfaceID; + char *ifname = InterfaceNameForID(m, q->InterfaceID); CacheUsed++; if (q->ThisQInterval) CacheActive++; LogMsgNoIdent("%6d%6d %-7s%s%s %5d %-6s%##s%s", i, n, - info ? info->ifname : mDNSOpaque16IsZero(q->TargetQID) ? "" : "-U-", + ifname ? ifname : mDNSOpaque16IsZero(q->TargetQID) ? "" : "-U-", mDNSOpaque16IsZero(q->TargetQID) ? (q->LongLived ? "l" : " ") : (q->LongLived ? "L" : "O"), q->AuthInfo ? "P" : " ", q->CurrentAnswers,