RelativePath="stdafx.cpp"\r
>\r
</File>\r
- <File\r
- RelativePath=".\StdioFileEx.cpp"\r
- >\r
- </File>\r
<File\r
RelativePath="ThirdPage.cpp"\r
>\r
RelativePath="stdafx.h"\r
>\r
</File>\r
- <File\r
- RelativePath=".\StdioFileEx.h"\r
- >\r
- </File>\r
<File\r
RelativePath="ThirdPage.h"\r
>\r
#include "PrinterSetupWizardApp.h"
#include "PrinterSetupWizardSheet.h"
#include "ThirdPage.h"
-#include "StdioFileEx.h"
#include <dns_sd.h>
#include <tcpxcv.h>
#include <winspool.h>
// 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 <ctype.h>
#include <stdio.h> // For stdout, stderr
#include <stdlib.h> // For exit()
include /Developer/Makefiles/pb_makefiles/platform.make
-MVERS = "mDNSResponder-214"
+MVERS = "mDNSResponder-214.3"
DDNSWRITECONFIG = "$(DSTROOT)/Library/Application Support/Bonjour/ddnswriteconfig"
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
Change History (most recent first):
$Log: DNSCommon.h,v $
+svn merge: Revision 1.75 2009/07/21 23:35:01 cheshire
+<rdar://problem/6434656> 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
<rdar://problem/3476350> Return negative answers when host knows authoritatively that no answer exists
Added definitions for RRTypeAnswersQuestionType/RRAssertsNonexistence/AnyTypeRecordAnswersQuestion
// 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);
Change History (most recent first):
$Log: mDNS.c,v $
+Revision 1.977 2009/07/23 23:30:01 cheshire
+<rdar://problem/7086623> Sleep Proxy: Ten-second maintenance wake not long enough to reliably get network connectivity
+
+Revision 1.976 2009/07/23 09:15:06 cheshire
+<rdar://problem/6434656> 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
+<rdar://problem/6434656> 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
+<rdar://problem/6434656> 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
+<rdar://problem/6434656> 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
+<rdar://problem/6434656> 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
+<rdar://problem/6434656> Sleep Proxy: Put owner OPT records in multicast announcements to avoid conflicts
+
Revision 1.970.2.1 2009/07/23 23:36:04 cheshire
<rdar://problem/7086623> Sleep Proxy: Ten-second maintenance wake not long enough to reliably get network connectivity
#define NO_HINFO 1
-mDNSlocal const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)~0;
// Any records bigger than this are considered 'large' records
#define SmallRecordLimit 1024
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 : "<NULL InterfaceID>");
+ return(intf ? intf->ifname : mDNSNULL);
}
// For a single given DNSQuestion, deliver an add/remove result for the single given AuthRecord
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)
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;
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
// 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);
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)
{
// 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
{
// 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)
{
// 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);
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",
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;
{
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 = "<NULL InterfaceID>";
+ 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)
// 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)
// 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);
}
}
{
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;
}
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",
{
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));
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;
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;
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
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;
{
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.,
// 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);
}
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;
}
}
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
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 = "<NULL InterfaceID>";
+ 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)
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;
#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;
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 (i<end-(mDNSu8 *)pkt)
+ while (i<end - (mDNSu8 *)pkt)
{
char buffer[128];
char *p = buffer + mDNS_snprintf(buffer, sizeof(buffer), "%04X", i);
- do if (i<end-(mDNSu8 *)pkt) p += mDNS_snprintf(p, sizeof(buffer), " %02X", ((mDNSu8 *)pkt)[i]); while (++i & 15);
+ do if (i<end - (mDNSu8 *)pkt) p += mDNS_snprintf(p, sizeof(buffer), " %02X", ((mDNSu8 *)pkt)[i]); while (++i & 15);
LogInfo("%s", buffer);
}
}
}
}
+mDNSinline mDNSs32 PenaltyTimeForServer(mDNS *m, DNSServer *server)
+ {
+ mDNSs32 ptime = 0;
+ if (server->penaltyTime != 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);
}
// 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
#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)
// 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 = "<NULL InterfaceID>";
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 ";
(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);
}
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 = "<NULL InterfaceID>";
+
+ 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);
}
}
{
#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;
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' <http://www.ietf.org/rfc/rfc2408.txt>
+ // ExchangeType == 34 means 'IKE_SA_INIT' <http://www.iana.org/assignments/ikev2-parameters>
+ 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;
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 = "<NULL InterfaceID>";
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);
}
m->SleepState = SleepState_Awake;
m->SleepSeqNum = 0;
m->SystemWakeOnLANEnabled = mDNSfalse;
+ m->SentSleepProxyRegistration = mDNSfalse;
+ m->AnnounceOwner = 0;
m->DelaySleep = 0;
m->SleepLimit = 0;
// 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);
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)
{
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);
}
}
// 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);
}
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);
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))
{
Change History (most recent first):
$Log: mDNSEmbeddedAPI.h,v $
+Revision 1.577 2009/07/16 00:34:18 cheshire
+<rdar://problem/6434656> 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
+<rdar://problem/6434656> Sleep Proxy: Put owner OPT records in multicast announcements to avoid conflicts
+
Revision 1.575 2009/07/11 01:57:00 cheshire
<rdar://problem/6613674> Sleep Proxy: Add support for using sleep proxy in local network interface hardware
Added declaration of ActivateLocalProxy
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;
((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'
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;
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()
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
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;
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")
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
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
#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
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
// 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
(*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 <null>, 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 <null>", 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 <null>", q->qname.c, DNSTypeName(q->qtype));
+ }
}
+ q->unansweredQueries = 0;
}
// ***************************************************************************
// 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))
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
{
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;
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;
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);
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
+ // <rdar://problem/6935929> 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);
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)
{
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
{
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;
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);
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;
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;
nexte = CheckServiceRegistrations(m);
if (nexte - m->NextuDNSEvent < 0) m->NextuDNSEvent = nexte;
+
+ nexte = CheckDNSServerPenalties(m);
+ if (nexte - m->NextuDNSEvent < 0) m->NextuDNSEvent = nexte;
}
// ***************************************************************************
#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
// 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);
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>com.apple.mDNSResponderHelper</string>
+ <key>OnDemand</key>
+ <false/>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/usr/sbin/mDNSResponderHelper</string>
+ <string>-t</string>
+ <string>0</string>
+ </array>
+ <key>ServiceIPC</key>
+ <false/>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>com.apple.mDNSResponder</string>
+ <key>OnDemand</key>
+ <false/>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/usr/sbin/mDNSResponder</string>
+ <string>-launchdaemon</string>
+ </array>
+ <key>ServiceIPC</key>
+ <false/>
+</dict>
+</plist>
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 -
{
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;
{
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)" :
if (!strcasecmp(argv[i], "-UnicastPacketLogging" )) mDNS_PacketLoggingEnabled = mDNStrue;
if (!strcasecmp(argv[i], "-OfferSleepProxyService" ))
OfferSleepProxyService = (i+1<argc && mDNSIsDigit(argv[i+1][0]) && mDNSIsDigit(argv[i+1][1]) && argv[i+1][2]==0) ? atoi(argv[++i]) : 80;
+ if (!strcasecmp(argv[i], "-StrictUnicastOrdering" )) StrictUnicastOrdering = mDNStrue;
}
// Note that mDNSPlatformInit will set DivertMulticastAdvertisements in the mDNS structure
#ifndef NO_SECURITYFRAMEWORK
// We should normally be running as a system daemon. However, that might not be the case in some scenarios (e.g. debugging).
// Explicitly ensure that our Keychain operations utilize the system domain.
- SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
+ if (opt_debug) SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
#endif
gPort = checkin(kmDNSHelperServiceName);
if (!gPort)
static const char config2[] =
"\";\n"
" nonce_size 16;\n"
- " lifetime time 5 min;\n"
+ " lifetime time 15 min;\n"
" initial_contact on;\n"
" support_proxy on;\n"
" nat_traversal force;\n"
" hash_algorithm sha1;\n"
" authentication_method pre_shared_key;\n"
" dh_group 2;\n"
- " lifetime time 5 min;\n"
+ " lifetime time 15 min;\n"
" }\n"
"}\n\n"
"sainfo anonymous { \n"
static const char config[] =
"%s"
"remote %s [%u] {\n"
+ " disconnect_on_idle idle_timeout 600 idle_direction idle_outbound;\n"
" exchange_mode aggressive;\n"
" doi ipsec_doi;\n"
" situation identity_only;\n"
" my_identifier user_fqdn \"dns:%s\";\n"
" shared_secret keychain \"dns:%s\";\n"
" nonce_size 16;\n"
- " lifetime time 5 min;\n"
+ " lifetime time 15 min;\n"
" initial_contact on;\n"
" support_proxy on;\n"
" nat_traversal force;\n"
" hash_algorithm sha1;\n"
" authentication_method pre_shared_key;\n"
" dh_group 2;\n"
- " lifetime time 5 min;\n"
+ " lifetime time 15 min;\n"
" }\n"
"}\n\n"
"sainfo address %s any address %s any {\n"
return -1;
}
+mDNSexport NetworkInterfaceInfoOSX *IfindexToInterfaceInfoOSX(const mDNS *const m, mDNSInterfaceID ifindex)
+{
+ mDNSu32 scope_id = (mDNSu32)(uintptr_t)ifindex;
+ NetworkInterfaceInfoOSX *i;
+
+ // Don't get tricked by inactive interfaces
+ for (i = m->p->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);
}
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);
}
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;
// 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",
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)
{
{
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);
sock->err = err;
KQueueSet(sock->fd, EV_ADD, EVFILT_READ, &sock->kqEntry);
}
- LogInfo("spawnSSLHandshake %p: done", sock);
+ debugf("spawnSSLHandshake %p: done", sock);
return err;
}
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
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);
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++;
}
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
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
// 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)
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);
}
}
// 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);
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));
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);
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
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.
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)
{
// 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.
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
}
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(); }
}
}
u_int BPF_len;
CFSocketRef BPF_cfs;
CFRunLoopSourceRef BPF_rls;
+ NetworkInterfaceInfoOSX *Registered; // non-NULL means registered with mDNS Core
};
struct mDNS_PlatformSupport_struct
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);
isa = PBXAggregateTarget;
buildConfigurationList = 03067D730C83A3CB0022BE1F /* Build configuration list for PBXAggregateTarget "Build Some" */;
buildPhases = (
+ FF045B6A0C7E4AA600448140 /* ShellScript */,
);
dependencies = (
03067D680C83A3830022BE1F /* PBXTargetDependency */,
2E0405ED0C31955500F13B59 /* Sources */,
2E0405EE0C31955500F13B59 /* Frameworks */,
4AAE0C5A0C68E6EC003882A5 /* CopyFiles */,
- FF045B6A0C7E4AA600448140 /* ShellScript */,
);
buildRules = (
);
);
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;
);
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;
);
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;
);
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;
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 = "";
*/
#ifndef _DNS_SD_H
-#define _DNS_SD_H 2140000
+#define _DNS_SD_H 2140300
#ifdef __cplusplus
extern "C" {
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))
{
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));
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),
{
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,