if ( treeBrowseList.currentIndex == 0 )
{
- window._content.location="http://www.apple.com/bonjour";
+ window._content.location="http://www.apple.com/macosx/features/bonjour";
}
else
{
if (errorCode)
{
if (errorCode == kDNSServiceErr_NoSuchRecord) printf("No Such Record");
- else printf("Error code %d", errorCode);
+ else if (errorCode == kDNSServiceErr_Timeout)
+ {
+ printf("No Such Record\n");
+ printf("Query Timed Out\n");
+ exit(1);
+ }
}
printf("\n");
if (addrs) freeaddrinfo(addrs);
}
-static DNSServiceErrorType RegisterProxyAddressRecord(DNSServiceRef sdref, const char *host, const char *ip)
+static DNSServiceErrorType RegisterProxyAddressRecord(DNSServiceRef sdref, const char *host, const char *ip, DNSServiceFlags flags)
{
// Call getip() after the call DNSServiceCreateConnection().
// On the Win32 platform, WinSock must be initialized for getip() to succeed.
// DNSServiceCreateConnection() is called before getip() is.
struct sockaddr_storage hostaddr;
getip(ip, &hostaddr);
+ flags |= kDNSServiceFlagsUnique;
if (hostaddr.ss_family == AF_INET)
- return(DNSServiceRegisterRecord(sdref, &record, kDNSServiceFlagsUnique, opinterface, host,
+ return(DNSServiceRegisterRecord(sdref, &record, flags, opinterface, host,
kDNSServiceType_A, kDNSServiceClass_IN, 4, &((struct sockaddr_in *)&hostaddr)->sin_addr, 240, MyRegisterRecordCallback, (void*)host));
else if (hostaddr.ss_family == AF_INET6)
- return(DNSServiceRegisterRecord(sdref, &record, kDNSServiceFlagsUnique, opinterface, host,
+ return(DNSServiceRegisterRecord(sdref, &record, flags, opinterface, host,
kDNSServiceType_AAAA, kDNSServiceClass_IN, 16, &((struct sockaddr_in6*)&hostaddr)->sin6_addr, 240, MyRegisterRecordCallback, (void*)host));
else return(kDNSServiceErr_BadParam);
}
#define HexPair(P) ((HexVal((P)[0]) << 4) | HexVal((P)[1]))
static DNSServiceErrorType RegisterService(DNSServiceRef *sdref,
- const char *nam, const char *typ, const char *dom, const char *host, const char *port, int argc, char **argv)
+ const char *nam, const char *typ, const char *dom, const char *host, const char *port, int argc, char **argv, DNSServiceFlags flags)
{
- DNSServiceFlags flags = 0;
uint16_t PortAsNumber = atoi(port);
Opaque16 registerPort = { { PortAsNumber >> 8, PortAsNumber & 0xFF } };
unsigned char txt[2048] = "";
//flags |= kDNSServiceFlagsAllowRemoteQuery;
//flags |= kDNSServiceFlagsNoAutoRename;
-
+
return(DNSServiceRegister(sdref, flags, opinterface, nam, typ, dom, host, registerPort.NotAnInteger, (uint16_t) (ptr-txt), txt, reg_reply, NULL));
}
DNSServiceErrorType err;
char buffer[TypeBufferSize], *typ, *dom;
int opi;
+ DNSServiceFlags flags = 0;
// Extract the program name from argv[0], which by convention contains the path to this executable.
// Note that this is just a voluntary convention, not enforced by the kernel --
printf("Using P2P\n");
}
+ if (argc > 1 && !strcasecmp(argv[1], "-includep2p"))
+ {
+ argc--;
+ argv++;
+ flags |= kDNSServiceFlagsIncludeP2P;
+ printf("Including P2P\n");
+ }
+
if (argc > 2 && !strcmp(argv[1], "-i"))
{
opinterface = if_nametoindex(argv[2]);
}
if (argc < 2) goto Fail; // Minimum command line is the command name and one argument
- operation = getfirstoption(argc, argv, "EFBZLlRPQqCAUNTMISV"
+ operation = getfirstoption(argc, argv, "EFBZLlRPQqtCAUNTMISV"
#if HAS_NAT_PMP_API
"X"
#endif
typ = gettype(buffer, typ);
if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string
printf("Browsing for %s%s%s\n", typ, dom[0] ? "." : "", dom);
- err = DNSServiceBrowse(&client, 0, opinterface, typ, dom, browse_reply, NULL);
+ err = DNSServiceBrowse(&client, flags, opinterface, typ, dom, browse_reply, NULL);
break;
case 'Z': typ = (argc < opi+1) ? "" : argv[opi+0];
dom = (argc < opi+3) ? "" : argv[opi+2];
typ = gettype(buffer, typ);
if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string
- err = RegisterService(&client, argv[opi+0], typ, dom, NULL, argv[opi+3], argc-(opi+4), argv+(opi+4));
+ err = RegisterService(&client, argv[opi+0], typ, dom, NULL, argv[opi+3], argc-(opi+4), argv+(opi+4), flags);
break;
case 'P': if (argc < opi+6) goto Fail;
err = DNSServiceCreateConnection(&client_pa);
if (err) { fprintf(stderr, "DNSServiceCreateConnection returned %d\n", err); return(err); }
- err = RegisterProxyAddressRecord(client_pa, argv[opi+4], argv[opi+5]);
- //err = RegisterProxyAddressRecord(client_pa, "two", argv[opi+5]);
+ err = RegisterProxyAddressRecord(client_pa, argv[opi+4], argv[opi+5], flags);
if (err) break;
- err = RegisterService(&client, argv[opi+0], gettype(buffer, argv[opi+1]), argv[opi+2], argv[opi+4], argv[opi+3], argc-(opi+6), argv+(opi+6));
- //DNSServiceRemoveRecord(client_pa, record, 0);
- //DNSServiceRemoveRecord(client_pa, record, 0);
+ err = RegisterService(&client, argv[opi+0], gettype(buffer, argv[opi+1]), argv[opi+2], argv[opi+4], argv[opi+3], argc-(opi+6), argv+(opi+6), flags);
break;
+ case 't':
case 'q':
case 'Q':
case 'C': {
uint16_t rrtype, rrclass;
- DNSServiceFlags flags = kDNSServiceFlagsReturnIntermediates;
+ flags |= kDNSServiceFlagsReturnIntermediates;
if (operation == 'q') flags |= kDNSServiceFlagsSuppressUnusable;
+ if (operation == 't') flags |= (kDNSServiceFlagsSuppressUnusable | kDNSServiceFlagsTimeout);
if (argc < opi+1) goto Fail;
rrtype = (argc <= opi+1) ? kDNSServiceType_A : GetRRType(argv[opi+1]);
rrclass = (argc <= opi+2) ? kDNSServiceClass_IN : atoi(argv[opi+2]);
static const char TXT1[] = "\xC" "First String" "\xD" "Second String" "\xC" "Third String";
static const char TXT2[] = "\xD" "Fourth String" "\xC" "Fifth String" "\xC" "Sixth String";
printf("Registering Service Test._testdualtxt._tcp.local.\n");
- err = DNSServiceRegister(&client, 0, opinterface, "Test", "_testdualtxt._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT1)-1, TXT1, reg_reply, NULL);
- if (!err) err = DNSServiceAddRecord(client, &record, 0, kDNSServiceType_TXT, sizeof(TXT2)-1, TXT2, 0);
+ err = DNSServiceRegister(&client, flags, opinterface, "Test", "_testdualtxt._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT1)-1, TXT1, reg_reply, NULL);
+ if (!err) err = DNSServiceAddRecord(client, &record, flags, kDNSServiceType_TXT, sizeof(TXT2)-1, TXT2, 0);
break;
}
<Tool\r
Name="VCCLCompilerTool"\r
Optimization="0"\r
- AdditionalIncludeDirectories="../../mDNSWindows/SystemService;../../mDNSWindows;../../mDNSShared;../../mDNSCore;"C:/Program Files/Microsoft SDKs/Windows/v6.1/Include""\r
+ AdditionalIncludeDirectories="../../mDNSWindows/SystemService;../../mDNSWindows;../../mDNSShared;../../mDNSCore;"$(VCInstallDir)include";"$(VCInstallDir)atlmfc\include";"C:/Program Files/Microsoft SDKs/Windows/v6.1/Include""\r
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;_WIN32_WINNT=0x0501;MDNS_DEBUGMSGS=0;TARGET_OS_WIN32;WIN32_LEAN_AND_MEAN;USE_TCP_LOOPBACK;PLATFORM_NO_STRSEP;PLATFORM_NO_EPIPE;PLATFORM_NO_RLIMIT;PID_FILE="""";UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1;_USE_32BIT_TIME_T"\r
MinimalRebuild="true"\r
BasicRuntimeChecks="3"\r
/>\r
<Tool\r
Name="VCCLCompilerTool"\r
- AdditionalIncludeDirectories="../../mDNSWindows/SystemService;../../mDNSWindows;../../mDNSShared;../../mDNSCore;"C:/Program Files/Microsoft SDKs/Windows/v6.1/Include""\r
+ AdditionalIncludeDirectories="../../mDNSWindows/SystemService;../../mDNSWindows;../../mDNSShared;../../mDNSCore;"$(VCInstallDir)include";"$(VCInstallDir)atlmfc\include";"C:/Program Files/Microsoft SDKs/Windows/v6.1/Include""\r
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_WIN32_WINNT=0x0501;MDNS_DEBUGMSGS=0;TARGET_OS_WIN32;WIN32_LEAN_AND_MEAN;USE_TCP_LOOPBACK;PLATFORM_NO_STRSEP;PLATFORM_NO_EPIPE;PLATFORM_NO_RLIMIT;PID_FILE="""";UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1;_USE_32BIT_TIME_T"\r
RuntimeLibrary="2"\r
UsePrecompiledHeader="0"\r
include /Developer/Makefiles/pb_makefiles/platform.make
-MVERS = "mDNSResponder-258.21"
+MVERS = "mDNSResponder-320.5"
DDNSWRITECONFIG = "$(DSTROOT)/Library/Application Support/Bonjour/ddnswriteconfig"
ditto . "$(SRCROOT)"
installhdrs::
- cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild installhdrs OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) -target SystemLibraries
+ cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild installhdrs OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) SDKROOT=$(SDKROOT) -target SystemLibraries
clean::
echo clean
// Set up a AuthRecord with sensible default values.
// These defaults may be overwritten with new values before mDNS_Register is called
mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID,
- mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, mDNSRecordCallback Callback, void *Context)
+ mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, AuthRecType artype, mDNSRecordCallback Callback, void *Context)
{
+ //
+ // LocalOnly auth record can be created with LocalOnly InterfaceID or a valid InterfaceID.
+ // Most of the applications normally create with LocalOnly InterfaceID and we store them as
+ // such, so that we can deliver the response to questions that specify LocalOnly InterfaceID.
+ // LocalOnly resource records can also be created with valid InterfaceID which happens today
+ // when we create LocalOnly records for /etc/hosts.
+
+ if (InterfaceID == mDNSInterface_LocalOnly && artype != AuthRecordLocalOnly)
+ {
+ LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch LocalOnly record InterfaceID %p called with artype %d", InterfaceID, artype);
+ return;
+ }
+ else if (InterfaceID == mDNSInterface_P2P && artype != AuthRecordP2P)
+ {
+ LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch P2P record InterfaceID %p called with artype %d", InterfaceID, artype);
+ return;
+ }
+ else if (!InterfaceID && (artype == AuthRecordP2P || artype == AuthRecordLocalOnly))
+ {
+ LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch InterfaceAny record InterfaceID %p called with artype %d", InterfaceID, artype);
+ return;
+ }
+
// Don't try to store a TTL bigger than we can represent in platform time units
if (ttl > 0x7FFFFFFFUL / mDNSPlatformOneSecond)
ttl = 0x7FFFFFFFUL / mDNSPlatformOneSecond;
rr->AddressProxy = zeroAddr;
rr->TimeRcvd = 0;
rr->TimeExpire = 0;
+ rr->ARType = artype;
// Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal)
// Field Group 4: Transient uDNS state for Authoritative Records (set in mDNS_Register_internal)
q->ForceMCast = mDNSfalse;
q->ReturnIntermed = mDNSfalse;
q->SuppressUnusable = mDNSfalse;
- q->WakeOnResolve = mDNSfalse;
+ q->SearchListIndex = 0;
+ q->AppendSearchDomains = 0;
+ q->RetryWithSearchDomains = mDNSfalse;
+ q->TimeoutQuestion = 0;
+ q->WakeOnResolve = 0;
+ q->qnameOrig = mDNSNULL;
q->QuestionCallback = callback;
q->QuestionContext = context;
}
mDNSexport mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
{
+ // LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records
+ // are handled in LocalOnlyRecordAnswersQuestion
+ if ((rr->InterfaceID == mDNSInterface_LocalOnly) || (rr->InterfaceID == mDNSInterface_P2P))
+ {
+ LogMsg("SameNameRecordAnswersQuestion: ERROR!! called with LocalOnly ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID);
+ return mDNSfalse;
+ }
if (rr->InterfaceID &&
q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly &&
rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
{
+ // LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records
+ // are handled in LocalOnlyRecordAnswersQuestion
+ if ((rr->InterfaceID == mDNSInterface_LocalOnly) || (rr->InterfaceID == mDNSInterface_P2P))
+ {
+ LogMsg("ResourceRecordAnswersQuestion: ERROR!! called with LocalOnly/P2P ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID);
+ return mDNSfalse;
+ }
+
if (rr->InterfaceID &&
q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly &&
rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
if (!rr->InterfaceID && rr->rDNSServer != q->qDNSServer) return(mDNSfalse);
// If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question.
- // This also covers the case where the ResourceRecord is mDNSInterface_LocalOnly and the question is expecting a unicast
- // DNS response. We don't want a local process to be able to create a fake LocalOnly address record for "www.bigbank.com"
- // which would then cause other applications (e.g. Safari) to connect to the wrong address. If we decide to support this later,
- // the restrictions need to be at least as strict as the restrictions on who can edit /etc/hosts and put fake addresses there.
if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
// RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
}
+// We have a separate function to handle LocalOnly AuthRecords because they can be created with
+// a valid InterfaceID (e.g., scoped /etc/hosts) and can be used to answer unicast questions unlike
+// multicast resource records (which has a valid InterfaceID) which can't be used to answer
+// unicast questions. ResourceRecordAnswersQuestion/SameNameRecordAnswersQuestion can't tell whether
+// a resource record is multicast or LocalOnly by just looking at the ResourceRecord because
+// LocalOnly records are truly identified by ARType in the AuthRecord. As P2P and LocalOnly record
+// are kept in the same hash table, we use the same function to make it easy for the callers when
+// they walk the hash table to answer LocalOnly/P2P questions
+//
+mDNSexport mDNSBool LocalOnlyRecordAnswersQuestion(AuthRecord *const ar, const DNSQuestion *const q)
+ {
+ ResourceRecord *rr = &ar->resrec;
+
+ // mDNSInterface_Any questions can be answered with LocalOnly/P2P records in this function. AuthRecord_Any
+ // records are handled in ResourceRecordAnswersQuestion/SameNameRecordAnswersQuestion
+ if (RRAny(ar))
+ {
+ LogMsg("LocalOnlyRecordAnswersQuestion: ERROR!! called with regular AuthRecordAny %##s", rr->name->c);
+ return mDNSfalse;
+ }
+
+ // Questions with mDNSInterface_LocalOnly InterfaceID should be answered with all resource records that are
+ // *local* to the machine. These include resource records that have InterfaceID set to mDNSInterface_LocalOnly,
+ // mDNSInterface_Any and any other real InterfaceID. Hence, LocalOnly questions should not be checked against
+ // the InterfaceID in the resource record.
+ //
+ // mDNSInterface_Unicast does not indicate any scope and hence treat them like mDNSInterface_Any.
+
+ if (rr->InterfaceID &&
+ q->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly && q->InterfaceID != mDNSInterface_Unicast &&
+ rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
+
+ // Entries in /etc/hosts are added as LocalOnly resource records. The LocalOnly resource records
+ // may have a scope e.g., fe80::1%en0. The question may be scoped or not: the InterfaceID may be set
+ // to mDNSInterface_Any, mDNSInterface_LocalOnly or a real InterfaceID (scoped).
+ //
+ // 1) Question: Any, LocalOnly Record: no scope. This question should be answered with this record.
+ //
+ // 2) Question: Any, LocalOnly Record: scoped. This question should be answered with the record because
+ // traditionally applications never specify scope e.g., getaddrinfo, but need to be able
+ // to get to /etc/hosts entries.
+ //
+ // 3) Question: Scoped (LocalOnly or InterfaceID), LocalOnly Record: no scope. This is the inverse of (2).
+ // If we register a LocalOnly record, we need to answer a LocalOnly question. If the /etc/hosts has a
+ // non scoped entry, it may not make sense to answer a scoped question. But we can't tell these two
+ // cases apart. As we currently answer LocalOnly question with LocalOnly record, we continue to do so.
+ //
+ // 4) Question: Scoped (LocalOnly or InterfaceID), LocalOnly Record: scoped. LocalOnly questions should be
+ // answered with any resource record where as if it has a valid InterfaceID, the scope should match.
+ //
+ // (1) and (2) is bypassed because we check for a non-NULL InterfaceID above. For (3), the InterfaceID is NULL
+ // and hence bypassed above. For (4) we bypassed LocalOnly questions and checked the scope of the record
+ // against the question.
+ //
+ // For P2P, InterfaceIDs of the question and the record should match.
+
+ // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question.
+ // LocalOnly authoritative answers are exempt. LocalOnly authoritative answers are used for /etc/host entries.
+ // We don't want a local process to be able to create a fake LocalOnly address record for "www.bigbank.com" which would then
+ // cause other applications (e.g. Safari) to connect to the wrong address. The rpc to register records filters out records
+ // with names that don't end in local and have mDNSInterface_LocalOnly set.
+ //
+ // Note: The check is bypassed for LocalOnly and for P2P it is not needed as only .local records are registered and for
+ // a question to match its names, it also has to end in .local and that question can't be a unicast question (See
+ // Question_uDNS macro and its usage). As P2P does not enforce .local only registrations we still make this check
+ // and also makes it future proof.
+
+ if (ar->ARType != AuthRecordLocalOnly && rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
+
+ // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
+ if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse);
+ if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
+
+ return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
+ }
+
mDNSexport mDNSBool AnyTypeRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
{
+ // LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records
+ // are handled in LocalOnlyRecordAnswersQuestion
+ if ((rr->InterfaceID == mDNSInterface_LocalOnly) || (rr->InterfaceID == mDNSInterface_P2P))
+ {
+ LogMsg("AnyTypeRecordAnswersQuestion: ERROR!! called with LocalOnly ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID);
+ return mDNSfalse;
+ }
if (rr->InterfaceID &&
q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly &&
rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
mDNSexport mDNSu8 *putPrereqNameNotInUse(const domainname *const name, DNSMessage *const msg, mDNSu8 *const ptr, mDNSu8 *const end)
{
AuthRecord prereq;
- mDNS_SetupResourceRecord(&prereq, mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, mDNSNULL, mDNSNULL);
+ mDNS_SetupResourceRecord(&prereq, mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, AuthRecordAny, mDNSNULL, mDNSNULL);
AssignDomainName(&prereq.namestorage, name);
prereq.resrec.rrtype = kDNSQType_ANY;
prereq.resrec.rrclass = kDNSClass_NONE;
mDNSexport mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease)
{
AuthRecord rr;
- mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
+ mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
rr.resrec.rrclass = NormalMaxDNSMessageData;
rr.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record
rr.resrec.rdestimate = sizeof(rdataOPT);
mDNSexport mDNSu8 *putUpdateLeaseWithLimit(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease, mDNSu8 *limit)
{
AuthRecord rr;
- mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
+ mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
rr.resrec.rrclass = NormalMaxDNSMessageData;
rr.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record
rr.resrec.rdestimate = sizeof(rdataOPT);
mDNSu8 *h = hinfo.rdatastorage.u.data;
mDNSu16 len = 2 + m->HIHardware.c[0] + m->HISoftware.c[0];
mDNSu8 *newptr;
- mDNS_SetupResourceRecord(&hinfo, mDNSNULL, mDNSInterface_Any, kDNSType_HINFO, 0, kDNSRecordTypeUnique, mDNSNULL, mDNSNULL);
+ mDNS_SetupResourceRecord(&hinfo, mDNSNULL, mDNSInterface_Any, kDNSType_HINFO, 0, kDNSRecordTypeUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
AppendDomainLabel(&hinfo.namestorage, &m->hostlabel);
AppendDomainName (&hinfo.namestorage, &authInfo->domain);
hinfo.resrec.rroriginalttl = 0;
}
if (m->NewLocalOnlyQuestions) return(m->timenow);
if (m->NewLocalRecords && AnyLocalRecordReady(m)) return(m->timenow);
+ if (m->NewLocalOnlyRecords) return(m->timenow);
if (m->SPSProxyListChanged) return(m->timenow);
if (m->LocalRemoveEvents) return(m->timenow);
if (e - m->NextScheduledProbe > 0) e = m->NextScheduledProbe;
if (e - m->NextScheduledResponse > 0) e = m->NextScheduledResponse;
}
-
+ if (e - m->NextScheduledStopTime > 0) e = m->NextScheduledStopTime;
return(e);
}
mDNSexport void ShowTaskSchedulingError(mDNS *const m)
{
+ AuthRecord *rr;
mDNS_Lock(m);
LogMsg("Task Scheduling Error: Continuously busy for more than a second");
if (m->NewLocalRecords)
{
- AuthRecord *rr = AnyLocalRecordReady(m);
+ rr = AnyLocalRecordReady(m);
if (rr) LogMsg("Task Scheduling Error: NewLocalRecords %s", ARDisplayString(m, rr));
}
+
+ if (m->NewLocalOnlyRecords) LogMsg("Task Scheduling Error: NewLocalOnlyRecords");
if (m->SPSProxyListChanged) LogMsg("Task Scheduling Error: SPSProxyListChanged");
if (m->LocalRemoveEvents) LogMsg("Task Scheduling Error: LocalRemoveEvents");
extern mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q);
extern mDNSBool AnyTypeRecordAnswersQuestion (const ResourceRecord *const rr, const DNSQuestion *const q);
extern mDNSBool UnicastResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q);
+extern mDNSBool LocalOnlyRecordAnswersQuestion(AuthRecord *const rr, const DNSQuestion *const q);
extern mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate);
extern mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd);
#pragma mark - DNS Message Parsing Functions
#endif
+#define AuthHashSlot(X) (DomainNameHashValue(X) % AUTH_HASH_SLOTS)
#define HashSlot(X) (DomainNameHashValue(X) % CACHE_HASH_SLOTS)
extern mDNSu32 DomainNameHashValue(const domainname *const name);
extern void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 rdlength);
MD5_Update(&c, (mDNSu8 *)msg, (unsigned long)(*end - (mDNSu8 *)msg));
// Construct TSIG RR, digesting variables as apporpriate
- mDNS_SetupResourceRecord(&tsig, mDNSNULL, 0, kDNSType_TSIG, 0, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
+ mDNS_SetupResourceRecord(&tsig, mDNSNULL, 0, kDNSType_TSIG, 0, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
// key name
AssignDomainName(&tsig.namestorage, &info->keyname);
mDNSlocal void BeginSleepProcessing(mDNS *const m);
mDNSlocal void RetrySPSRegistrations(mDNS *const m);
mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *EthAddr, mDNSOpaque48 *password);
+mDNSlocal mDNSBool CacheRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q);
+mDNSlocal mDNSBool LocalRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q);
mDNSlocal void mDNS_PurgeBeforeResolve(mDNS *const m, DNSQuestion *q);
// ***************************************************************************
#pragma mark - General Utility Functions
#endif
+// If there is a authoritative LocalOnly record that answers questions of type A, AAAA and CNAME
+// this returns true. Main use is to handle /etc/hosts records.
+#define LORecordAnswersAddressType(rr) ((rr)->ARType == AuthRecordLocalOnly && \
+ (rr)->resrec.RecordType & kDNSRecordTypeUniqueMask && \
+ ((rr)->resrec.rrtype == kDNSType_A || (rr)->resrec.rrtype == kDNSType_AAAA || \
+ (rr)->resrec.rrtype == kDNSType_CNAME))
+
+#define FollowCNAME(q, rr, AddRecord) (AddRecord && (q)->qtype != kDNSType_CNAME && \
+ (rr)->RecordType != kDNSRecordTypePacketNegative && \
+ (rr)->rrtype == kDNSType_CNAME)
+
+mDNSlocal void SetNextQueryStopTime(mDNS *const m, const DNSQuestion *const q)
+ {
+ if (m->mDNS_busy != m->mDNS_reentrancy+1)
+ LogMsg("SetNextQueryTime: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+
+#if ForceAlerts
+ if (m->mDNS_busy != m->mDNS_reentrancy+1) *(long*)0 = 0;
+#endif
+
+ if (m->NextScheduledStopTime - q->StopTime > 0)
+ m->NextScheduledStopTime = q->StopTime;
+ }
+
mDNSexport void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q)
{
if (m->mDNS_busy != m->mDNS_reentrancy+1)
}
}
+mDNSlocal void ReleaseAuthEntity(AuthHash *r, AuthEntity *e)
+ {
+#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING >= 1
+ unsigned int i;
+ for (i=0; i<sizeof(*e); i++) ((char*)e)[i] = 0xFF;
+#endif
+ e->next = r->rrauth_free;
+ r->rrauth_free = e;
+ r->rrauth_totalused--;
+ }
+
+mDNSlocal void ReleaseAuthGroup(AuthHash *r, AuthGroup **cp)
+ {
+ AuthEntity *e = (AuthEntity *)(*cp);
+ LogMsg("ReleaseAuthGroup: Releasing AuthGroup %##s", (*cp)->name->c);
+ if ((*cp)->rrauth_tail != &(*cp)->members)
+ LogMsg("ERROR: (*cp)->members == mDNSNULL but (*cp)->rrauth_tail != &(*cp)->members)");
+ if ((*cp)->name != (domainname*)((*cp)->namestorage)) mDNSPlatformMemFree((*cp)->name);
+ (*cp)->name = mDNSNULL;
+ *cp = (*cp)->next; // Cut record from list
+ ReleaseAuthEntity(r, e);
+ }
+
+mDNSlocal AuthEntity *GetAuthEntity(AuthHash *r, const AuthGroup *const PreserveAG)
+ {
+ AuthEntity *e = mDNSNULL;
+
+ if (r->rrauth_lock) { LogMsg("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL); }
+ r->rrauth_lock = 1;
+
+ if (!r->rrauth_free)
+ {
+ // We allocate just one AuthEntity at a time because we need to be able
+ // free them all individually which normally happens when we parse /etc/hosts into
+ // AuthHash where we add the "new" entries and discard (free) the already added
+ // entries. If we allocate as chunks, we can't free them individually.
+ AuthEntity *storage = mDNSPlatformMemAllocate(sizeof(AuthEntity));
+ storage->next = mDNSNULL;
+ r->rrauth_free = storage;
+ }
+
+ // If we still have no free records, recycle all the records we can.
+ // Enumerating the entire auth is moderately expensive, so when we do it, we reclaim all the records we can in one pass.
+ if (!r->rrauth_free)
+ {
+ mDNSu32 oldtotalused = r->rrauth_totalused;
+ mDNSu32 slot;
+ for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+ {
+ AuthGroup **cp = &r->rrauth_hash[slot];
+ while (*cp)
+ {
+ if ((*cp)->members || (*cp)==PreserveAG) cp=&(*cp)->next;
+ else ReleaseAuthGroup(r, cp);
+ }
+ }
+ LogInfo("GetAuthEntity: Recycled %d records to reduce auth cache from %d to %d",
+ oldtotalused - r->rrauth_totalused, oldtotalused, r->rrauth_totalused);
+ }
+
+ if (r->rrauth_free) // If there are records in the free list, take one
+ {
+ e = r->rrauth_free;
+ r->rrauth_free = e->next;
+ if (++r->rrauth_totalused >= r->rrauth_report)
+ {
+ LogInfo("RR Auth now using %ld objects", r->rrauth_totalused);
+ if (r->rrauth_report < 100) r->rrauth_report += 10;
+ else if (r->rrauth_report < 1000) r->rrauth_report += 100;
+ else r->rrauth_report += 1000;
+ }
+ mDNSPlatformMemZero(e, sizeof(*e));
+ }
+
+ r->rrauth_lock = 0;
+
+ return(e);
+ }
+
+mDNSexport AuthGroup *AuthGroupForName(AuthHash *r, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name)
+ {
+ AuthGroup *ag;
+ for (ag = r->rrauth_hash[slot]; ag; ag=ag->next)
+ if (ag->namehash == namehash && SameDomainName(ag->name, name))
+ break;
+ return(ag);
+ }
+
+mDNSexport AuthGroup *AuthGroupForRecord(AuthHash *r, const mDNSu32 slot, const ResourceRecord *const rr)
+ {
+ return(AuthGroupForName(r, slot, rr->namehash, rr->name));
+ }
+
+mDNSlocal AuthGroup *GetAuthGroup(AuthHash *r, const mDNSu32 slot, const ResourceRecord *const rr)
+ {
+ mDNSu16 namelen = DomainNameLength(rr->name);
+ AuthGroup *ag = (AuthGroup*)GetAuthEntity(r, mDNSNULL);
+ if (!ag) { LogMsg("GetAuthGroup: Failed to allocate memory for %##s", rr->name->c); return(mDNSNULL); }
+ ag->next = r->rrauth_hash[slot];
+ ag->namehash = rr->namehash;
+ ag->members = mDNSNULL;
+ ag->rrauth_tail = &ag->members;
+ ag->name = (domainname*)ag->namestorage;
+ ag->NewLocalOnlyRecords = mDNSNULL;
+ if (namelen > InlineCacheGroupNameSize) ag->name = mDNSPlatformMemAllocate(namelen);
+ if (!ag->name)
+ {
+ LogMsg("GetAuthGroup: Failed to allocate name storage for %##s", rr->name->c);
+ ReleaseAuthEntity(r, (AuthEntity*)ag);
+ return(mDNSNULL);
+ }
+ AssignDomainName(ag->name, rr->name);
+
+ if (AuthGroupForRecord(r, slot, rr)) LogMsg("GetAuthGroup: Already have AuthGroup for %##s", rr->name->c);
+ r->rrauth_hash[slot] = ag;
+ if (AuthGroupForRecord(r, slot, rr) != ag) LogMsg("GetAuthGroup: Not finding AuthGroup for %##s", rr->name->c);
+
+ return(ag);
+ }
+
+// Returns the AuthGroup in which the AuthRecord was inserted
+mDNSexport AuthGroup *InsertAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr)
+ {
+ AuthGroup *ag;
+ const mDNSu32 slot = AuthHashSlot(rr->resrec.name);
+ ag = AuthGroupForRecord(r, slot, &rr->resrec);
+ if (!ag) ag = GetAuthGroup(r, slot, &rr->resrec); // If we don't have a AuthGroup for this name, make one now
+ if (ag)
+ {
+ LogInfo("InsertAuthRecord: inserting auth record %s from table", ARDisplayString(m, rr));
+ *(ag->rrauth_tail) = rr; // Append this record to tail of cache slot list
+ ag->rrauth_tail = &(rr->next); // Advance tail pointer
+ }
+ return ag;
+ }
+
+mDNSexport AuthGroup *RemoveAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr)
+ {
+ AuthGroup *a;
+ AuthGroup **ag = &a;
+ AuthRecord **rp;
+ const mDNSu32 slot = AuthHashSlot(rr->resrec.name);
+
+ a = AuthGroupForRecord(r, slot, &rr->resrec);
+ if (!a) { LogMsg("RemoveAuthRecord: ERROR!! AuthGroup not found for %s", ARDisplayString(m, rr)); return mDNSNULL; }
+ rp = &(*ag)->members;
+ while (*rp)
+ {
+ if (*rp != rr)
+ rp=&(*rp)->next;
+ else
+ {
+ // We don't break here, so that we can set the tail below without tracking "prev" pointers
+
+ LogInfo("RemoveAuthRecord: removing auth record %s from table", ARDisplayString(m, rr));
+ *rp = (*rp)->next; // Cut record from list
+ }
+ }
+ // TBD: If there are no more members, release authgroup ?
+ (*ag)->rrauth_tail = rp;
+ return a;
+ }
+
mDNSexport CacheGroup *CacheGroupForName(const mDNS *const m, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name)
{
CacheGroup *cg;
return(intf ? intf->ifname : mDNSNULL);
}
-// For a single given DNSQuestion, deliver an add/remove result for the single given AuthRecord
-// Used by AnswerAllLocalQuestionsWithLocalAuthRecord() and AnswerNewLocalOnlyQuestion()
-mDNSlocal void AnswerLocalQuestionWithLocalAuthRecord(mDNS *const m, DNSQuestion *q, AuthRecord *rr, QC_result AddRecord)
+// Caller should hold the lock
+mDNSlocal void GenerateNegativeResponse(mDNS *const m)
{
+ DNSQuestion *q;
+ if (!m->CurrentQuestion) { LogMsg("GenerateNegativeResponse: ERROR!! CurrentQuestion not set"); return; }
+ q = m->CurrentQuestion;
+ LogInfo("GenerateNegativeResponse: Generating negative response for question %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+
+ MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, mDNSInterface_Any, mDNSNULL);
+ AnswerCurrentQuestionWithResourceRecord(m, &m->rec.r, QC_addnocache);
+ if (m->CurrentQuestion == q) { q->ThisQInterval = 0; } // Deactivate this question
+ // Don't touch the question after this
+ m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
+ }
+
+mDNSlocal void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, ResourceRecord *rr)
+ {
+ const mDNSBool selfref = SameDomainName(&q->qname, &rr->rdata->u.name);
+ if (q->CNAMEReferrals >= 10 || selfref)
+ LogMsg("AnswerQuestionByFollowingCNAME: %p %##s (%s) NOT following CNAME referral %d%s for %s",
+ q, q->qname.c, DNSTypeName(q->qtype), q->CNAMEReferrals, selfref ? " (Self-Referential)" : "", RRDisplayString(m, rr));
+ else
+ {
+ const mDNSu32 c = q->CNAMEReferrals + 1; // Stash a copy of the new q->CNAMEReferrals value
+
+ // The SameDomainName check above is to ignore bogus CNAME records that point right back at
+ // themselves. Without that check we can get into a case where we have two duplicate questions,
+ // A and B, and when we stop question A, UpdateQuestionDuplicates copies the value of CNAMEReferrals
+ // from A to B, and then A is re-appended to the end of the list as a duplicate of B (because
+ // the target name is still the same), and then when we stop question B, UpdateQuestionDuplicates
+ // copies the B's value of CNAMEReferrals back to A, and we end up not incrementing CNAMEReferrals
+ // for either of them. This is not a problem for CNAME loops of two or more records because in
+ // those cases the newly re-appended question A has a different target name and therefore cannot be
+ // a duplicate of any other question ('B') which was itself a duplicate of the previous question A.
+
+ // Right now we just stop and re-use the existing query. If we really wanted to be 100% perfect,
+ // and track CNAMEs coming and going, we should really create a subordinate query here,
+ // which we would subsequently cancel and retract if the CNAME referral record were removed.
+ // In reality this is such a corner case we'll ignore it until someone actually needs it.
+
+ LogInfo("AnswerQuestionByFollowingCNAME: %p %##s (%s) following CNAME referral %d for %s",
+ q, q->qname.c, DNSTypeName(q->qtype), q->CNAMEReferrals, RRDisplayString(m, rr));
+
+ mDNS_StopQuery_internal(m, q); // Stop old query
+ AssignDomainName(&q->qname, &rr->rdata->u.name); // Update qname
+ q->qnamehash = DomainNameHashValue(&q->qname); // and namehash
+ // If a unicast query results in a CNAME that points to a .local, we need to re-try
+ // this as unicast. Setting the mDNSInterface_Unicast tells mDNS_StartQuery_internal
+ // to try this as unicast query even though it is a .local name
+ if (!mDNSOpaque16IsZero(q->TargetQID) && IsLocalDomain(&q->qname))
+ {
+ LogInfo("AnswerQuestionByFollowingCNAME: Resolving a .local CNAME %p %##s (%s) Record %s",
+ q, q->qname.c, DNSTypeName(q->qtype), RRDisplayString(m, rr));
+ q->InterfaceID = mDNSInterface_Unicast;
+ }
+ mDNS_StartQuery_internal(m, q); // start new query
+ // Record how many times we've done this. We need to do this *after* mDNS_StartQuery_internal,
+ // because mDNS_StartQuery_internal re-initializes CNAMEReferrals to zero
+ q->CNAMEReferrals = c;
+ }
+ }
+
+// For a single given DNSQuestion pointed to by CurrentQuestion, deliver an add/remove result for the single given AuthRecord
+// Note: All the callers should use the m->CurrentQuestion to see if the question is still valid or not
+mDNSlocal void AnswerLocalQuestionWithLocalAuthRecord(mDNS *const m, AuthRecord *rr, QC_result AddRecord)
+ {
+ DNSQuestion *q = m->CurrentQuestion;
+ mDNSBool followcname;
+
+ if (!q)
+ {
+ LogMsg("AnswerLocalQuestionWithLocalAuthRecord: ERROR!! CurrentQuestion NULL while answering with %s", ARDisplayString(m, rr));
+ return;
+ }
+
+ followcname = FollowCNAME(q, &rr->resrec, AddRecord);
+
// We should not be delivering results for record types Unregistered, Deregistering, and (unverified) Unique
if (!(rr->resrec.RecordType & kDNSRecordTypeActiveMask))
{
if (q->QuestionCallback && !q->NoAnswer)
{
q->CurrentAnswers += AddRecord ? 1 : -1;
- q->QuestionCallback(m, q, &rr->resrec, AddRecord);
+ if (LORecordAnswersAddressType(rr))
+ {
+ if (!followcname || q->ReturnIntermed)
+ {
+ // Don't send this packet on the wire as we answered from /etc/hosts
+ q->ThisQInterval = 0;
+ q->LOAddressAnswers += AddRecord ? 1 : -1;
+ q->QuestionCallback(m, q, &rr->resrec, AddRecord);
+ }
+ mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again
+ // The callback above could have caused the question to stop. Detect that
+ // using m->CurrentQuestion
+ if (followcname && m->CurrentQuestion == q)
+ AnswerQuestionByFollowingCNAME(m, q, &rr->resrec);
+ return;
+ }
+ else
+ q->QuestionCallback(m, q, &rr->resrec, AddRecord);
}
mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again
}
+mDNSlocal void AnswerInterfaceAnyQuestionsWithLocalAuthRecord(mDNS *const m, AuthRecord *rr, QC_result AddRecord)
+ {
+ if (m->CurrentQuestion)
+ LogMsg("AnswerInterfaceAnyQuestionsWithLocalAuthRecord: ERROR m->CurrentQuestion already set: %##s (%s)",
+ m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
+ m->CurrentQuestion = m->Questions;
+ while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
+ {
+ mDNSBool answered;
+ DNSQuestion *q = m->CurrentQuestion;
+ if (RRAny(rr))
+ answered = ResourceRecordAnswersQuestion(&rr->resrec, q);
+ else
+ answered = LocalOnlyRecordAnswersQuestion(rr, q);
+ if (answered)
+ AnswerLocalQuestionWithLocalAuthRecord(m, rr, AddRecord); // MUST NOT dereference q again
+ if (m->CurrentQuestion == q) // If m->CurrentQuestion was not auto-advanced, do it ourselves now
+ m->CurrentQuestion = q->next;
+ }
+ m->CurrentQuestion = mDNSNULL;
+ }
+
// When a new local AuthRecord is created or deleted, AnswerAllLocalQuestionsWithLocalAuthRecord()
// delivers the appropriate add/remove events to listening questions:
// 1. It runs though all our LocalOnlyQuestions delivering answers as appropriate,
m->CurrentQuestion = m->LocalOnlyQuestions;
while (m->CurrentQuestion && m->CurrentQuestion != m->NewLocalOnlyQuestions)
{
+ mDNSBool answered;
DNSQuestion *q = m->CurrentQuestion;
- m->CurrentQuestion = q->next;
- if (ResourceRecordAnswersQuestion(&rr->resrec, q))
- AnswerLocalQuestionWithLocalAuthRecord(m, q, rr, AddRecord); // MUST NOT dereference q again
- }
-
- // If this AuthRecord is marked LocalOnly or P2P, then we want to deliver it to all local 'mDNSInterface_Any' questions
- if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->resrec.InterfaceID == mDNSInterface_P2P)
- {
- m->CurrentQuestion = m->Questions;
- while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
- {
- DNSQuestion *q = m->CurrentQuestion;
+ // We are called with both LocalOnly/P2P record or a regular AuthRecord
+ if (RRAny(rr))
+ answered = ResourceRecordAnswersQuestion(&rr->resrec, q);
+ else
+ answered = LocalOnlyRecordAnswersQuestion(rr, q);
+ if (answered)
+ AnswerLocalQuestionWithLocalAuthRecord(m, rr, AddRecord); // MUST NOT dereference q again
+ if (m->CurrentQuestion == q) // If m->CurrentQuestion was not auto-advanced, do it ourselves now
m->CurrentQuestion = q->next;
- if (ResourceRecordAnswersQuestion(&rr->resrec, q))
- AnswerLocalQuestionWithLocalAuthRecord(m, q, rr, AddRecord); // MUST NOT dereference q again
- }
}
m->CurrentQuestion = mDNSNULL;
+
+ // If this AuthRecord is marked LocalOnly or P2P, then we want to deliver it to all local 'mDNSInterface_Any' questions
+ if (rr->ARType == AuthRecordLocalOnly || rr->ARType == AuthRecordP2P)
+ AnswerInterfaceAnyQuestionsWithLocalAuthRecord(m, rr, AddRecord);
+
}
// ***************************************************************************
if (!target) LogInfo("SetTargetToHostName: Don't know how to set the target of rrtype %s", DNSTypeName(rr->resrec.rrtype));
- if (!(rr->ForceMCast || rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->resrec.InterfaceID == mDNSInterface_P2P || IsLocalDomain(&rr->namestorage)))
+ if (!(rr->ForceMCast || rr->ARType == AuthRecordLocalOnly || rr->ARType == AuthRecordP2P || IsLocalDomain(&rr->namestorage)))
{
const domainname *const n = SetUnicastTargetToHostName(m, rr);
if (n) newname = n;
#define RecordIsLocalDuplicate(A,B) \
((A)->resrec.InterfaceID == (B)->resrec.InterfaceID && RecordLDT((A),(B)) && IdenticalResourceRecord(&(A)->resrec, &(B)->resrec))
+mDNSlocal AuthRecord *CheckAuthIdenticalRecord(AuthHash *r, AuthRecord *rr)
+ {
+ AuthGroup *a;
+ AuthGroup **ag = &a;
+ AuthRecord **rp;
+ const mDNSu32 slot = AuthHashSlot(rr->resrec.name);
+
+ a = AuthGroupForRecord(r, slot, &rr->resrec);
+ if (!a) return mDNSNULL;
+ rp = &(*ag)->members;
+ while (*rp)
+ {
+ if (!RecordIsLocalDuplicate(*rp, rr))
+ rp=&(*rp)->next;
+ else
+ {
+ if ((*rp)->resrec.RecordType == kDNSRecordTypeDeregistering)
+ {
+ (*rp)->AnnounceCount = 0;
+ rp=&(*rp)->next;
+ }
+ else return *rp;
+ }
+ }
+ return (mDNSNULL);
+ }
+
+mDNSlocal mDNSBool CheckAuthRecordConflict(AuthHash *r, AuthRecord *rr)
+ {
+ AuthGroup *a;
+ AuthGroup **ag = &a;
+ AuthRecord **rp;
+ const mDNSu32 slot = AuthHashSlot(rr->resrec.name);
+
+ a = AuthGroupForRecord(r, slot, &rr->resrec);
+ if (!a) return mDNSfalse;
+ rp = &(*ag)->members;
+ while (*rp)
+ {
+ const AuthRecord *s1 = rr->RRSet ? rr->RRSet : rr;
+ const AuthRecord *s2 = (*rp)->RRSet ? (*rp)->RRSet : *rp;
+ if (s1 != s2 && SameResourceRecordSignature((*rp), rr) && !IdenticalSameNameRecord(&(*rp)->resrec, &rr->resrec))
+ return mDNStrue;
+ else
+ rp=&(*rp)->next;
+ }
+ return (mDNSfalse);
+ }
+
+// checks to see if "rr" is already present
+mDNSlocal AuthRecord *CheckAuthSameRecord(AuthHash *r, AuthRecord *rr)
+ {
+ AuthGroup *a;
+ AuthGroup **ag = &a;
+ AuthRecord **rp;
+ const mDNSu32 slot = AuthHashSlot(rr->resrec.name);
+
+ a = AuthGroupForRecord(r, slot, &rr->resrec);
+ if (!a) return mDNSNULL;
+ rp = &(*ag)->members;
+ while (*rp)
+ {
+ if (*rp != rr)
+ rp=&(*rp)->next;
+ else
+ {
+ return *rp;
+ }
+ }
+ return (mDNSNULL);
+ }
+
// Exported so uDNS.c can call this
mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr)
{
if (m->DivertMulticastAdvertisements && !AuthRecord_uDNS(rr))
{
mDNSInterfaceID previousID = rr->resrec.InterfaceID;
- if (rr->resrec.InterfaceID == mDNSInterface_Any || rr->resrec.InterfaceID == mDNSInterface_P2P) rr->resrec.InterfaceID = mDNSInterface_LocalOnly;
+ if (rr->resrec.InterfaceID == mDNSInterface_Any || rr->resrec.InterfaceID == mDNSInterface_P2P)
+ {
+ rr->resrec.InterfaceID = mDNSInterface_LocalOnly;
+ rr->ARType = AuthRecordLocalOnly;
+ }
if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly)
{
NetworkInterfaceInfo *intf = FirstInterfaceForID(m, rr->resrec.InterfaceID);
- if (intf && !intf->Advertise) rr->resrec.InterfaceID = mDNSInterface_LocalOnly;
+ if (intf && !intf->Advertise){ rr->resrec.InterfaceID = mDNSInterface_LocalOnly; rr->ARType = AuthRecordLocalOnly; }
}
if (rr->resrec.InterfaceID != previousID)
LogInfo("mDNS_Register_internal: Diverting record to local-only %s", ARDisplayString(m, rr));
}
- while (*p && *p != rr) p=&(*p)->next;
+ if (RRLocalOnly(rr))
+ {
+ if (CheckAuthSameRecord(&m->rrauth, rr))
+ {
+ LogMsg("mDNS_Register_internal: ERROR!! Tried to register LocalOnly AuthRecord %p %##s (%s) that's already in the list",
+ rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+ return(mStatus_AlreadyRegistered);
+ }
+ }
+ else
+ {
+ while (*p && *p != rr) p=&(*p)->next;
+ if (*p)
+ {
+ LogMsg("mDNS_Register_internal: ERROR!! Tried to register AuthRecord %p %##s (%s) that's already in the list",
+ rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+ return(mStatus_AlreadyRegistered);
+ }
+ }
+
while (*d && *d != rr) d=&(*d)->next;
- if (*d || *p)
+ if (*d)
{
- LogMsg("Error! Tried to register AuthRecord %p %##s (%s) that's already in the list",
- rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+ LogMsg("mDNS_Register_internal: ERROR!! Tried to register AuthRecord %p %##s (%s) that's already in the Duplicate list",
+ rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
return(mStatus_AlreadyRegistered);
}
rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
return(mStatus_Invalid);
}
- if (!(rr->DependentOn->resrec.RecordType & (kDNSRecordTypeUnique | kDNSRecordTypeVerified)))
+ if (!(rr->DependentOn->resrec.RecordType & (kDNSRecordTypeUnique | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique)))
{
LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn->RecordType bad type %X",
rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->DependentOn->resrec.RecordType);
}
}
- // If this resource record is referencing a specific interface, make sure it exists
- if (rr->resrec.InterfaceID && rr->resrec.InterfaceID != mDNSInterface_LocalOnly && rr->resrec.InterfaceID != mDNSInterface_P2P)
+ // If this resource record is referencing a specific interface, make sure it exists.
+ // Skip checks for LocalOnly and P2P as they are not valid InterfaceIDs. Also, for scoped
+ // entries in /etc/hosts skip that check as that interface may not be valid at this time.
+ if (rr->resrec.InterfaceID && rr->ARType != AuthRecordLocalOnly && rr->ARType != AuthRecordP2P)
{
NetworkInterfaceInfo *intf = FirstInterfaceForID(m, rr->resrec.InterfaceID);
if (!intf)
rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
rr->resrec.rdatahash = target ? DomainNameHashValue(target) : RDataHashValue(&rr->resrec);
- if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->resrec.InterfaceID == mDNSInterface_P2P)
+ if (RRLocalOnly(rr))
{
- // If this is supposed to be unique, make sure we don't have any name conflicts
+ // If this is supposed to be unique, make sure we don't have any name conflicts.
+ // If we found a conflict, we may still want to insert the record in the list but mark it appropriately
+ // (kDNSRecordTypeDeregistering) so that we deliver RMV events to the application. But this causes more
+ // complications and not clear whether there are any benefits. See rdar:9304275 for details.
+ // Hence, just bail out.
if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
{
- const AuthRecord *s1 = rr->RRSet ? rr->RRSet : rr;
- for (r = m->ResourceRecords; r; r=r->next)
+ if (CheckAuthRecordConflict(&m->rrauth, rr))
{
- const AuthRecord *s2 = r->RRSet ? r->RRSet : r;
- if (s1 != s2 && SameResourceRecordSignature(r, rr) && !IdenticalSameNameRecord(&r->resrec, &rr->resrec))
- break;
- }
- if (r) // If we found a conflict, set RecordType = kDNSRecordTypeDeregistering so we'll deliver the callback
- {
- debugf("Name conflict %p %##s (%s)", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
- rr->resrec.RecordType = kDNSRecordTypeDeregistering;
- rr->resrec.rroriginalttl = 0;
- rr->ImmedAnswer = mDNSInterfaceMark;
- m->LocalRemoveEvents = mDNStrue;
- m->NextScheduledResponse = m->timenow;
+ LogInfo("mDNS_Register_internal: Name conflict %s (%p), InterfaceID %p", ARDisplayString(m, rr), rr, rr->resrec.InterfaceID);
+ return mStatus_NameConflict;
}
}
}
- // For uDNS records, we don't support duplicate checks at this time
+ // For uDNS records, we don't support duplicate checks at this time.
#ifndef UNICAST_DISABLED
if (AuthRecord_uDNS(rr))
{
#endif
// Now that we've finished building our new record, make sure it's not identical to one we already have
- for (r = m->ResourceRecords; r; r=r->next)
- if (RecordIsLocalDuplicate(r, rr))
- {
- if (r->resrec.RecordType == kDNSRecordTypeDeregistering) r->AnnounceCount = 0;
- else break;
- }
-
+ if (RRLocalOnly(rr))
+ {
+ rr->ProbeCount = 0;
+ rr->AnnounceCount = 0;
+ r = CheckAuthIdenticalRecord(&m->rrauth, rr);
+ }
+ else
+ {
+ for (r = m->ResourceRecords; r; r=r->next)
+ if (RecordIsLocalDuplicate(r, rr))
+ {
+ if (r->resrec.RecordType == kDNSRecordTypeDeregistering) r->AnnounceCount = 0;
+ else break;
+ }
+ }
+
if (r)
{
debugf("mDNS_Register_internal:Adding to duplicate list %s", ARDisplayString(m,rr));
else
{
debugf("mDNS_Register_internal: Adding to active record list %s", ARDisplayString(m,rr));
- if (!m->NewLocalRecords) m->NewLocalRecords = rr;
- *p = rr;
+ if (RRLocalOnly(rr))
+ {
+ AuthGroup *ag;
+ ag = InsertAuthRecord(m, &m->rrauth, rr);
+ if (ag && !ag->NewLocalOnlyRecords) {
+ m->NewLocalOnlyRecords = mDNStrue;
+ ag->NewLocalOnlyRecords = rr;
+ }
+ // No probing for LocalOnly records, Acknowledge them right away
+ if (rr->resrec.RecordType == kDNSRecordTypeUnique) rr->resrec.RecordType = kDNSRecordTypeVerified;
+ AcknowledgeRecord(m, rr);
+ return(mStatus_NoError);
+ }
+ else
+ {
+ if (!m->NewLocalRecords) m->NewLocalRecords = rr;
+ *p = rr;
+ }
}
if (!AuthRecord_uDNS(rr)) // This check is superfluous, given that for unicast records we (currently) bail out above
AuthRecord *r2;
mDNSu8 RecordType = rr->resrec.RecordType;
AuthRecord **p = &m->ResourceRecords; // Find this record in our list of active records
+ mDNSBool dupList = mDNSfalse;
- while (*p && *p != rr) p=&(*p)->next;
+ if (RRLocalOnly(rr))
+ {
+ AuthGroup *a;
+ AuthGroup **ag = &a;
+ AuthRecord **rp;
+ const mDNSu32 slot = AuthHashSlot(rr->resrec.name);
+
+ a = AuthGroupForRecord(&m->rrauth, slot, &rr->resrec);
+ if (!a) return mDNSfalse;
+ rp = &(*ag)->members;
+ while (*rp && *rp != rr) rp=&(*rp)->next;
+ p = rp;
+ }
+ else
+ {
+ while (*p && *p != rr) p=&(*p)->next;
+ }
if (*p)
{
debugf("mDNS_Register_internal: Duplicate record %p taking over from %p %##s (%s)",
dup, rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
*d = dup->next; // Cut replacement record from DuplicateRecords list
- dup->next = rr->next; // And then...
- rr->next = dup; // ... splice it in right after the record we're about to delete
+ if (RRLocalOnly(rr))
+ {
+ dup->next = mDNSNULL;
+ if (!InsertAuthRecord(m, &m->rrauth, dup)) LogMsg("mDNS_Deregister_internal: ERROR!! cannot insert %s", ARDisplayString(m, dup));
+ }
+ else
+ {
+ dup->next = rr->next; // And then...
+ rr->next = dup; // ... splice it in right after the record we're about to delete
+ }
dup->resrec.RecordType = rr->resrec.RecordType;
dup->ProbeCount = rr->ProbeCount;
dup->AnnounceCount = rr->AnnounceCount;
p = &m->DuplicateRecords;
while (*p && *p != rr) p=&(*p)->next;
// If we found our record on the duplicate list, then make sure we don't send a goodbye for it
- if (*p) rr->RequireGoodbye = mDNSfalse;
+ if (*p) { rr->RequireGoodbye = mDNSfalse; dupList = mDNStrue; }
if (*p) debugf("mDNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)",
rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
}
}
else
{
- *p = rr->next; // Cut this record from the list
+ if (!dupList && RRLocalOnly(rr))
+ {
+ AuthGroup *ag = RemoveAuthRecord(m, &m->rrauth, rr);
+ if (ag->NewLocalOnlyRecords == rr) ag->NewLocalOnlyRecords = rr->next;
+ }
+ else
+ {
+ *p = rr->next; // Cut this record from the list
+ if (m->NewLocalRecords == rr) m->NewLocalRecords = rr->next;
+ }
// If someone is about to look at this, bump the pointer forward
if (m->CurrentRecord == rr) m->CurrentRecord = rr->next;
- if (m->NewLocalRecords == rr) m->NewLocalRecords = rr->next;
rr->next = mDNSNULL;
// Should we generate local remove events here?
SendNDP(m, NDP_Adv, NDP_Override, r2, &r2->AddressProxy.ip.v6, &r2->WakeUp.IMAC, &AllHosts_v6, &AllHosts_v6_Eth);
}
r2->LastAPTime = m->timenow;
+ // After 15 wakeups without success (maybe host has left the network) send three goodbyes instead
if (--r2->AnnounceCount <= GoodbyeCount) r2->WakeUp.HMAC = zeroEthAddr;
}
}
// 3. Answers and announcements we need to send
for (rr = m->ResourceRecords; rr; rr=rr->next)
{
- if (rr->SendRNow == intf->InterfaceID)
+
+ // Skip this interface if the record InterfaceID is *Any and the record is not
+ // appropriate for the interface type.
+ if ((rr->SendRNow == intf->InterfaceID) &&
+ ((rr->resrec.InterfaceID == mDNSInterface_Any) && !mDNSPlatformValidRecordForInterface(rr, intf)))
+ {
+ LogInfo("SendResponses: Not Sending %s, on %s", ARDisplayString(m, rr), InterfaceNameForID(m, rr->SendRNow));
+ rr->SendRNow = GetNextActiveInterfaceID(intf);
+ }
+ else if (rr->SendRNow == intf->InterfaceID)
{
RData *OldRData = rr->resrec.rdata;
mDNSu16 oldrdlength = rr->resrec.rdlength;
if (rr->SendNSECNow == mDNSInterfaceMark || rr->SendNSECNow == intf->InterfaceID)
{
AuthRecord nsec;
- mDNS_SetupResourceRecord(&nsec, mDNSNULL, mDNSInterface_Any, kDNSType_NSEC, rr->resrec.rroriginalttl, kDNSRecordTypeUnique, mDNSNULL, mDNSNULL);
+ mDNS_SetupResourceRecord(&nsec, mDNSNULL, mDNSInterface_Any, kDNSType_NSEC, rr->resrec.rroriginalttl, kDNSRecordTypeUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
nsec.resrec.rrclass |= kDNSClass_UniqueRRSet;
AssignDomainName(&nsec.namestorage, rr->resrec.name);
mDNSPlatformMemZero(nsec.rdatastorage.u.nsec.bitmap, sizeof(nsec.rdatastorage.u.nsec.bitmap));
if (OwnerRecordSpace)
{
AuthRecord opt;
- mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
+ mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
opt.resrec.rrclass = NormalMaxDNSMessageData;
opt.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record
opt.resrec.rdestimate = sizeof(rdataOPT);
numAnnounce, numAnnounce == 1 ? "" : "s",
numAnswer, numAnswer == 1 ? "" : "s",
m->omsg.h.numAdditionals, m->omsg.h.numAdditionals == 1 ? "" : "s", intf->InterfaceID);
+
if (intf->IPv4Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, mDNSNULL, &AllDNSLinkGroup_v4, MulticastDNSPort, mDNSNULL, mDNSNULL);
if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, mDNSNULL, &AllDNSLinkGroup_v6, MulticastDNSPort, mDNSNULL, mDNSNULL);
if (!m->SuppressSending) m->SuppressSending = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+9)/10);
if (rr->SendRNow)
{
- if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly && rr->resrec.InterfaceID != mDNSInterface_P2P)
+ if (rr->ARType != AuthRecordLocalOnly && rr->ARType != AuthRecordP2P)
LogMsg("SendResponses: No active interface %p to send: %p %02X %s", rr->SendRNow, rr->resrec.InterfaceID, rr->resrec.RecordType, ARDisplayString(m, rr));
rr->SendRNow = mDNSNULL;
}
}
LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed WakeOnResolve name %##s", q->qname.c);
}
+
mDNSlocal mDNSBool AccelerateThisQuery(mDNS *const m, DNSQuestion *q)
{
if (OwnerRecordSpace)
{
AuthRecord opt;
- mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
+ mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
opt.resrec.rrclass = NormalMaxDNSMessageData;
opt.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record
opt.resrec.rdestimate = sizeof(rdataOPT);
for (ar = m->ResourceRecords; ar; ar=ar->next)
if (ar->SendRNow)
{
- if (ar->resrec.InterfaceID != mDNSInterface_LocalOnly && ar->resrec.InterfaceID != mDNSInterface_P2P)
+ if (ar->ARType != AuthRecordLocalOnly && ar->ARType != AuthRecordP2P)
LogMsg("SendQueries: No active interface %p to send probe: %p %s", ar->SendRNow, ar->resrec.InterfaceID, ARDisplayString(m, ar));
ar->SendRNow = mDNSNULL;
}
mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheRecord *const rr, const QC_result AddRecord)
{
DNSQuestion *const q = m->CurrentQuestion;
- mDNSBool followcname = rr->resrec.RecordType != kDNSRecordTypePacketNegative && AddRecord &&
- rr->resrec.rrtype == kDNSType_CNAME && q->qtype != kDNSType_CNAME;
+ mDNSBool followcname = FollowCNAME(q, &rr->resrec, AddRecord);
+
verbosedebugf("AnswerCurrentQuestionWithResourceRecord:%4lu %s TTL %d %s",
q->CurrentAnswers, AddRecord ? "Add" : "Rmv", rr->resrec.rroriginalttl, CRDisplayString(m, rr));
- if (QuerySuppressed(q)) return;
+ // Normally we don't send out the unicast query if we have answered using our local only auth records e.g., /etc/hosts.
+ // But if the query for "A" record has a local answer but query for "AAAA" record has no local answer, we might
+ // send the AAAA query out which will come back with CNAME and will also answer the "A" query. To prevent that,
+ // we check to see if that query already has a unique local answer.
+ if (q->LOAddressAnswers)
+ {
+ LogInfo("AnswerCurrentQuestionWithResourceRecord: Question %p %##s (%s) not answering with record %s due to "
+ "LOAddressAnswers %d", q, q->qname.c, DNSTypeName(q->qtype), ARDisplayString(m, rr),
+ q->LOAddressAnswers);
+ return;
+ }
+
+ if (QuerySuppressed(q))
+ {
+ // If the query is suppressed, then we don't want to answer from the cache. But if this query is
+ // supposed to time out, we still want to callback the clients. We do this only for TimeoutQuestions
+ // that are timing out, which we know are answered with Negative cache record when timing out.
+ if (!q->TimeoutQuestion || rr->resrec.RecordType != kDNSRecordTypePacketNegative || (m->timenow - q->StopTime < 0))
+ return;
+ }
// Note: Use caution here. In the case of records with rr->DelayDelivery set, AnswerCurrentQuestionWithResourceRecord(... mDNStrue)
// may be called twice, once when the record is received, and again when it's time to notify local clients.
// including starting/stopping queries, registering/deregistering records, etc.
if (followcname && m->CurrentQuestion == q)
- {
- const mDNSBool selfref = SameDomainName(&q->qname, &rr->resrec.rdata->u.name);
- if (q->CNAMEReferrals >= 10 || selfref)
- LogMsg("AnswerCurrentQuestionWithResourceRecord: %p %##s (%s) NOT following CNAME referral %d%s for %s",
- q, q->qname.c, DNSTypeName(q->qtype), q->CNAMEReferrals, selfref ? " (Self-Referential)" : "", CRDisplayString(m, rr));
- else
- {
- const mDNSu32 c = q->CNAMEReferrals + 1; // Stash a copy of the new q->CNAMEReferrals value
-
- // The SameDomainName check above is to ignore bogus CNAME records that point right back at
- // themselves. Without that check we can get into a case where we have two duplicate questions,
- // A and B, and when we stop question A, UpdateQuestionDuplicates copies the value of CNAMEReferrals
- // from A to B, and then A is re-appended to the end of the list as a duplicate of B (because
- // the target name is still the same), and then when we stop question B, UpdateQuestionDuplicates
- // copies the B's value of CNAMEReferrals back to A, and we end up not incrementing CNAMEReferrals
- // for either of them. This is not a problem for CNAME loops of two or more records because in
- // those cases the newly re-appended question A has a different target name and therefore cannot be
- // a duplicate of any other question ('B') which was itself a duplicate of the previous question A.
-
- // Right now we just stop and re-use the existing query. If we really wanted to be 100% perfect,
- // and track CNAMEs coming and going, we should really create a subordinate query here,
- // which we would subsequently cancel and retract if the CNAME referral record were removed.
- // In reality this is such a corner case we'll ignore it until someone actually needs it.
- LogInfo("AnswerCurrentQuestionWithResourceRecord: %p %##s (%s) following CNAME referral %d for %s",
- q, q->qname.c, DNSTypeName(q->qtype), q->CNAMEReferrals, CRDisplayString(m, rr));
-
- mDNS_StopQuery_internal(m, q); // Stop old query
- AssignDomainName(&q->qname, &rr->resrec.rdata->u.name); // Update qname
- q->qnamehash = DomainNameHashValue(&q->qname); // and namehash
- // If a unicast query results in a CNAME that points to a .local, we need to re-try
- // this as unicast. Setting the mDNSInterface_Unicast tells mDNS_StartQuery_internal
- // to try this as unicast query even though it is a .local name
- if (!mDNSOpaque16IsZero(q->TargetQID) && IsLocalDomain(&q->qname))
- {
- LogInfo("AnswerCurrentQuestionWithResourceRecord: Resolving a .local CNAME %p %##s (%s) CacheRecord %s",
- q, q->qname.c, DNSTypeName(q->qtype), CRDisplayString(m, rr));
- q->InterfaceID = mDNSInterface_Unicast;
- }
- mDNS_StartQuery_internal(m, q); // start new query
- // Record how many times we've done this. We need to do this *after* mDNS_StartQuery_internal,
- // because mDNS_StartQuery_internal re-initializes CNAMEReferrals to zero
- q->CNAMEReferrals = c;
- }
- }
+ AnswerQuestionByFollowingCNAME(m, q, &rr->resrec);
}
// New Questions are answered through AnswerNewQuestion. But there may not have been any
m->lock_rrcache = 0;
}
-// Caller should hold the lock
-mDNSlocal void AnswerSuppressUnusableQuestion(mDNS *const m, DNSQuestion *q)
- {
- LogInfo("AnswerSuppressUnusableQuestion: Generating negative response for question %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
- if (!m->CurrentQuestion) LogMsg("AnswerSuppressUnusableQuestion: ERROR!! CurrentQuestion not set");
-
- MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, mDNSInterface_Any, mDNSNULL);
- AnswerCurrentQuestionWithResourceRecord(m, &m->rec.r, QC_addnocache);
- if (m->CurrentQuestion == q) q->ThisQInterval = 0; // Deactivate this question
- // Don't touch the question after this
- m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
- }
-
mDNSlocal void AnswerNewQuestion(mDNS *const m)
{
mDNSBool ShouldQueryImmediately = mDNStrue;
DNSQuestion *const q = m->NewQuestions; // Grab the question we're going to answer
- const mDNSu32 slot = HashSlot(&q->qname);
+ mDNSu32 slot = HashSlot(&q->qname);
CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
+ AuthRecord *lr;
+ AuthGroup *ag;
+ mDNSBool AnsweredFromCache = mDNSfalse;
verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, mDNSInterface_Any, q->qDNSServer);
q->NoAnswer = NoAnswer_Normal; // Temporarily turn off answer suppression
AnswerCurrentQuestionWithResourceRecord(m, &m->rec.r, QC_addnocache);
- q->NoAnswer = NoAnswer_Fail; // Restore NoAnswer state
+ // Don't touch the question if it has been stopped already
+ if (m->CurrentQuestion == q) q->NoAnswer = NoAnswer_Fail; // Restore NoAnswer state
m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
}
if (m->CurrentQuestion != q) { LogInfo("AnswerNewQuestion: Question deleted while generating NoAnswer_Fail response"); goto exit; }
- // If 'mDNSInterface_Any' question, see if we want to tell it about LocalOnly records
- if (q->InterfaceID == mDNSInterface_Any)
+ // See if we want to tell it about LocalOnly records
+ if (m->CurrentRecord)
+ LogMsg("AnswerNewQuestion ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
+ slot = AuthHashSlot(&q->qname);
+ ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname);
+ if (ag)
{
- if (m->CurrentRecord)
- LogMsg("AnswerNewQuestion ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
- m->CurrentRecord = m->ResourceRecords;
- while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords)
+ m->CurrentRecord = ag->members;
+ while (m->CurrentRecord && m->CurrentRecord != ag->NewLocalOnlyRecords)
{
AuthRecord *rr = m->CurrentRecord;
m->CurrentRecord = rr->next;
- if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->resrec.InterfaceID == mDNSInterface_P2P)
- if (ResourceRecordAnswersQuestion(&rr->resrec, q))
+ //
+ // If the question is mDNSInterface_LocalOnly, all records local to the machine should be used
+ // to answer the query. This is handled in AnswerNewLocalOnlyQuestion.
+ //
+ // We handle mDNSInterface_Any and scoped questions here. See LocalOnlyRecordAnswersQuestion for more
+ // details on how we handle this case. For P2P we just handle "Interface_Any" questions. For LocalOnly
+ // we handle both mDNSInterface_Any and scoped questions.
+
+ if (rr->ARType == AuthRecordLocalOnly || (rr->ARType == AuthRecordP2P && q->InterfaceID == mDNSInterface_Any))
+ if (LocalOnlyRecordAnswersQuestion(rr, q))
{
- AnswerLocalQuestionWithLocalAuthRecord(m, q, rr, mDNStrue);
+ AnswerLocalQuestionWithLocalAuthRecord(m, rr, mDNStrue);
if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here
}
}
- m->CurrentRecord = mDNSNULL;
}
+ m->CurrentRecord = mDNSNULL;
+
if (m->CurrentQuestion != q) { LogInfo("AnswerNewQuestion: Question deleted while while giving LocalOnly record answers"); goto exit; }
+ if (q->LOAddressAnswers)
+ {
+ LogInfo("AnswerNewQuestion: Question %p %##s (%s) answered using local auth records LOAddressAnswers %d",
+ q, q->qname.c, DNSTypeName(q->qtype), q->LOAddressAnswers);
+ goto exit;
+ }
+
+ // Before we go check the cache and ship this query on the wire, we have to be sure that there are
+ // no local records that could possibly answer this question. As we did not check the NewLocalRecords, we
+ // need to just peek at them to see whether it will answer this question. If it would answer, pretend
+ // that we answered. AnswerAllLocalQuestionsWithLocalAuthRecord will answer shortly. This happens normally
+ // when we add new /etc/hosts entries and restart the question. It is a new question and also a new record.
+ if (ag)
+ {
+ lr = ag->NewLocalOnlyRecords;
+ while (lr)
+ {
+ if (LORecordAnswersAddressType(lr) && LocalOnlyRecordAnswersQuestion(lr, q))
+ {
+ LogInfo("AnswerNewQuestion: Question %p %##s (%s) will be answered using new local auth records "
+ " LOAddressAnswers %d", q, q->qname.c, DNSTypeName(q->qtype), q->LOAddressAnswers);
+ goto exit;
+ }
+ lr = lr->next;
+ }
+ }
+
+
// If we are not supposed to answer this question, generate a negative response.
// Temporarily suspend the SuppressQuery so that AnswerCurrentQuestionWithResourceRecord can answer the question
- if (QuerySuppressed(q)) { q->SuppressQuery = mDNSfalse; AnswerSuppressUnusableQuestion(m, q); q->SuppressQuery = mDNStrue; }
+ if (QuerySuppressed(q)) { q->SuppressQuery = mDNSfalse; GenerateNegativeResponse(m); q->SuppressQuery = mDNStrue; }
else
{
CacheRecord *rr;
q->CurrentAnswers++;
if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++;
if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++;
+ AnsweredFromCache = mDNStrue;
AnswerCurrentQuestionWithResourceRecord(m, rr, QC_add);
if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here
}
// it's not remotely remarkable, and therefore unlikely to be of much help tracking down bugs.
if (m->CurrentQuestion != q) { debugf("AnswerNewQuestion: Question deleted while giving cache answers"); goto exit; }
+ // Neither a local record nor a cache entry could answer this question. If this question need to be retried
+ // with search domains, generate a negative response which will now retry after appending search domains.
+ // If the query was suppressed above, we already generated a negative response. When it gets unsuppressed,
+ // we will retry with search domains.
+ if (!QuerySuppressed(q) && !AnsweredFromCache && q->RetryWithSearchDomains)
+ {
+ LogInfo("AnswerNewQuestion: Generating response for retrying with search domains %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+ GenerateNegativeResponse(m);
+ }
+
+ if (m->CurrentQuestion != q) { debugf("AnswerNewQuestion: Question deleted while giving negative answer"); goto exit; }
+
+ // Note: When a query gets suppressed or retried with search domains, we de-activate the question.
+ // Hence we don't execute the following block of code for those cases.
if (ShouldQueryImmediately && ActiveQuestion(q))
{
debugf("AnswerNewQuestion: ShouldQueryImmediately %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
}
// When a NewLocalOnlyQuestion is created, AnswerNewLocalOnlyQuestion runs though our ResourceRecords delivering any
-// appropriate answers, stopping if it reaches a NewLocalRecord -- these will be handled by AnswerAllLocalQuestionsWithLocalAuthRecord
+// appropriate answers, stopping if it reaches a NewLocalOnlyRecord -- these will be handled by AnswerAllLocalQuestionsWithLocalAuthRecord
mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m)
{
+ mDNSu32 slot;
+ AuthGroup *ag;
DNSQuestion *q = m->NewLocalOnlyQuestions; // Grab the question we're going to answer
m->NewLocalOnlyQuestions = q->next; // Advance NewLocalOnlyQuestions to the next (if any)
if (m->CurrentRecord)
LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
- m->CurrentRecord = m->ResourceRecords;
- while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords)
+ // 1. First walk the LocalOnly records answering the LocalOnly question
+ // 2. As LocalOnly questions should also be answered by any other Auth records local to the machine,
+ // walk the ResourceRecords list delivering the answers
+ slot = AuthHashSlot(&q->qname);
+ ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname);
+ if (ag)
{
- AuthRecord *rr = m->CurrentRecord;
- m->CurrentRecord = rr->next;
- if (ResourceRecordAnswersQuestion(&rr->resrec, q))
+ m->CurrentRecord = ag->members;
+ while (m->CurrentRecord && m->CurrentRecord != ag->NewLocalOnlyRecords)
{
- AnswerLocalQuestionWithLocalAuthRecord(m, q, rr, mDNStrue);
- if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here
+ AuthRecord *rr = m->CurrentRecord;
+ m->CurrentRecord = rr->next;
+ if (LocalOnlyRecordAnswersQuestion(rr, q))
+ {
+ AnswerLocalQuestionWithLocalAuthRecord(m, rr, mDNStrue);
+ if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here
+ }
+ }
+ }
+
+ if (m->CurrentQuestion == q)
+ {
+ m->CurrentRecord = m->ResourceRecords;
+
+ while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords)
+ {
+ AuthRecord *rr = m->CurrentRecord;
+ m->CurrentRecord = rr->next;
+ if (ResourceRecordAnswersQuestion(&rr->resrec, q))
+ {
+ AnswerLocalQuestionWithLocalAuthRecord(m, rr, mDNStrue);
+ if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here
+ }
}
}
if (m->NextScheduledSPS - rr->TimeExpire > 0)
m->NextScheduledSPS = rr->TimeExpire;
}
- else // else proxy record expired, so remove it
+ else // else proxy record expired, so remove it
+ {
+ LogSPS("CheckProxyRecords: Removing %d H-MAC %.6a I-MAC %.6a %d %s",
+ m->ProxyRecords, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, ARDisplayString(m, rr));
+ SetSPSProxyListChanged(rr->resrec.InterfaceID);
+ mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
+ // Don't touch rr after this -- memory may have been free'd
+ }
+ }
+ // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because
+ // new records could have been added to the end of the list as a result of that call.
+ if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now
+ m->CurrentRecord = rr->next;
+ }
+ }
+
+mDNSlocal void CheckRmvEventsForLocalRecords(mDNS *const m)
+ {
+ while (m->CurrentRecord)
+ {
+ AuthRecord *rr = m->CurrentRecord;
+ if (rr->AnsweredLocalQ && rr->resrec.RecordType == kDNSRecordTypeDeregistering)
+ {
+ debugf("CheckRmvEventsForLocalRecords: Generating local RMV events for %s", ARDisplayString(m, rr));
+ rr->resrec.RecordType = kDNSRecordTypeShared;
+ AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNSfalse);
+ if (m->CurrentRecord == rr) // If rr still exists in list, restore its state now
+ {
+ rr->resrec.RecordType = kDNSRecordTypeDeregistering;
+ rr->AnsweredLocalQ = mDNSfalse;
+ // SendResponses normally calls CompleteDeregistration after sending goodbyes.
+ // For LocalOnly records, we don't do that and hence we need to do that here.
+ if (RRLocalOnly(rr)) CompleteDeregistration(m, rr);
+ }
+ }
+ if (m->CurrentRecord == rr) // If m->CurrentRecord was not auto-advanced, do it ourselves now
+ m->CurrentRecord = rr->next;
+ }
+ }
+
+mDNSlocal void TimeoutQuestions(mDNS *const m)
+ {
+ m->NextScheduledStopTime = m->timenow + 0x3FFFFFFF;
+ if (m->CurrentQuestion)
+ LogMsg("TimeoutQuestions ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c,
+ DNSTypeName(m->CurrentQuestion->qtype));
+ m->CurrentQuestion = m->Questions;
+ while (m->CurrentQuestion)
+ {
+ DNSQuestion *const q = m->CurrentQuestion;
+ if (q->StopTime)
+ {
+ if (m->timenow - q->StopTime >= 0)
+ {
+ LogInfo("TimeoutQuestions: question %##s timed out, time %d", q->qname.c, m->timenow - q->StopTime);
+ GenerateNegativeResponse(m);
+ if (m->CurrentQuestion == q) q->StopTime = 0;
+ }
+ else
{
- LogSPS("CheckProxyRecords: Removing %d H-MAC %.6a I-MAC %.6a %d %s",
- m->ProxyRecords, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, ARDisplayString(m, rr));
- SetSPSProxyListChanged(rr->resrec.InterfaceID);
- mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
- // Don't touch rr after this -- memory may have been free'd
+ if (m->NextScheduledStopTime - q->StopTime > 0)
+ m->NextScheduledStopTime = q->StopTime;
}
}
- // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because
- // new records could have been added to the end of the list as a result of that call.
- if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now
- m->CurrentRecord = rr->next;
+ // If m->CurrentQuestion wasn't modified out from under us, advance it now
+ // We can't do this at the start of the loop because GenerateNegativeResponse
+ // depends on having m->CurrentQuestion point to the right question
+ if (m->CurrentQuestion == q)
+ m->CurrentQuestion = q->next;
}
+ m->CurrentQuestion = mDNSNULL;
}
mDNSexport mDNSs32 mDNS_Execute(mDNS *const m)
{
int i;
AuthRecord *head, *tail;
+ mDNSu32 slot;
+ AuthGroup *ag;
verbosedebugf("mDNS_Execute");
// 3. Purge our cache of stale old records
if (m->rrcache_size && m->timenow - m->NextCacheCheck >= 0)
{
- mDNSu32 slot, numchecked = 0;
+ mDNSu32 numchecked = 0;
m->NextCacheCheck = m->timenow + 0x3FFFFFFF;
for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
{
{
m->LocalRemoveEvents = mDNSfalse;
m->CurrentRecord = m->ResourceRecords;
- while (m->CurrentRecord)
- {
- AuthRecord *rr = m->CurrentRecord;
- if (rr->AnsweredLocalQ && rr->resrec.RecordType == kDNSRecordTypeDeregistering)
+ CheckRmvEventsForLocalRecords(m);
+ // Walk the LocalOnly records and deliver the RMV events
+ for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+ for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next)
{
- debugf("mDNS_Execute: Generating local RMV events for %s", ARDisplayString(m, rr));
- rr->resrec.RecordType = kDNSRecordTypeShared;
- AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNSfalse);
- if (m->CurrentRecord == rr) // If rr still exists in list, restore its state now
- {
- rr->resrec.RecordType = kDNSRecordTypeDeregistering;
- rr->AnsweredLocalQ = mDNSfalse;
- }
+ m->CurrentRecord = ag->members;
+ if (m->CurrentRecord) CheckRmvEventsForLocalRecords(m);
}
- if (m->CurrentRecord == rr) // If m->CurrentRecord was not auto-advanced, do it ourselves now
- m->CurrentRecord = rr->next;
- }
}
+
if (i >= 1000) LogMsg("mDNS_Execute: m->LocalRemoveEvents exceeded loop limit");
-
+
for (i=0; m->NewLocalOnlyQuestions && i<1000; i++) AnswerNewLocalOnlyQuestion(m);
if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewLocalOnlyQuestion exceeded loop limit");
if (i >= 1000) LogMsg("mDNS_Execute: m->NewLocalRecords exceeded loop limit");
+ // Check to see if we have any new LocalOnly/P2P records to examine for delivering
+ // to our local questions
+ if (m->NewLocalOnlyRecords)
+ {
+ m->NewLocalOnlyRecords = mDNSfalse;
+ for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+ for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next)
+ {
+ for (i=0; i<100 && ag->NewLocalOnlyRecords; i++)
+ {
+ AuthRecord *rr = ag->NewLocalOnlyRecords;
+ ag->NewLocalOnlyRecords = ag->NewLocalOnlyRecords->next;
+ // LocalOnly records should always be ready as they never probe
+ if (LocalRecordReady(rr))
+ {
+ debugf("mDNS_Execute: Delivering Add event with LocalAuthRecord %s", ARDisplayString(m, rr));
+ AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNStrue);
+ }
+ else LogMsg("mDNS_Execute: LocalOnlyRecord %s not ready", ARDisplayString(m, rr));
+ }
+ // We limit about 100 per AuthGroup that can be serviced at a time
+ if (i >= 100) LogMsg("mDNS_Execute: ag->NewLocalOnlyRecords exceeded loop limit");
+ }
+ }
+
// 5. Some questions may have picked a new DNS server and the cache may answer these questions now.
AnswerQuestionsForDNSServerChanges(m);
m->RandomQueryDelay = 0;
m->RandomReconfirmDelay = 0;
+ if (m->NextScheduledStopTime && m->timenow - m->NextScheduledStopTime >= 0) TimeoutQuestions(m);
#ifndef UNICAST_DISABLED
if (m->NextSRVUpdate && m->timenow - m->NextSRVUpdate >= 0) UpdateAllSRVRecords(m);
if (m->timenow - m->NextScheduledNATOp >= 0) CheckNATMappings(m);
{ q->ReqLease = 0; sendLLQRefresh(m, q); }
}
+mDNSlocal mDNSBool QuestionHasLocalAnswers(mDNS *const m, DNSQuestion *q)
+ {
+ AuthRecord *rr;
+ mDNSu32 slot;
+ AuthGroup *ag;
+
+ slot = AuthHashSlot(&q->qname);
+ ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname);
+ if (ag)
+ {
+ for (rr = ag->members; rr; rr=rr->next)
+ // Filter the /etc/hosts records - LocalOnly, Unique, A/AAAA/CNAME
+ if (LORecordAnswersAddressType(rr) && LocalOnlyRecordAnswersQuestion(rr, q))
+ {
+ LogInfo("QuestionHasLocalAnswers: Question %p %##s (%s) has local answer %s", q, q->qname.c, DNSTypeName(q->qtype), ARDisplayString(m, rr));
+ return mDNStrue;
+ }
+ }
+ return mDNSfalse;
+ }
+
// ActivateUnicastQuery() is called from three places:
// 1. When a new question is created
// 2. On wake from sleep
question->servPort = zeroIPPort;
if (question->tcp) { DisposeTCPConn(question->tcp); question->tcp = mDNSNULL; }
}
- if (ScheduleImmediately)
+ // If the question has local answers, then we don't want answers from outside
+ if (ScheduleImmediately && !QuestionHasLocalAnswers(m, question))
{
question->ThisQInterval = InitialQuestionInterval;
question->LastQTime = m->timenow - question->ThisQInterval;
}
}
+// Caller should hold the lock
+mDNSexport void mDNSCoreRestartAddressQueries(mDNS *const m, mDNSBool SearchDomainsChanged, FlushCache flushCacheRecords,
+ CallbackBeforeStartQuery BeforeStartCallback, void *context)
+ {
+ DNSQuestion *q;
+ DNSQuestion *restart = mDNSNULL;
+
+ if (!m->mDNS_busy) LogMsg("mDNSCoreRestartAddressQueries: ERROR!! Lock not held");
+
+ // 1. Flush the cache records
+ if (flushCacheRecords) flushCacheRecords(m);
+
+ // 2. Even though we may have purged the cache records above, before it can generate RMV event
+ // we are going to stop the question. Hence we need to deliver the RMV event before we
+ // stop the question.
+ //
+ // CurrentQuestion is used by RmvEventsForQuestion below. While delivering RMV events, the
+ // application callback can potentially stop the current question (detected by CurrentQuestion) or
+ // *any* other question which could be the next one that we may process here. RestartQuestion
+ // points to the "next" question which will be automatically advanced in mDNS_StopQuery_internal
+ // if the "next" question is stopped while the CurrentQuestion is stopped
+
+ if (m->RestartQuestion)
+ LogMsg("mDNSCoreRestartAddressQueries: ERROR!! m->RestartQuestion already set: %##s (%s)",
+ m->RestartQuestion->qname.c, DNSTypeName(m->RestartQuestion->qtype));
+
+ m->RestartQuestion = m->Questions;
+ while (m->RestartQuestion)
+ {
+ q = m->RestartQuestion;
+ m->RestartQuestion = q->next;
+ // GetZoneData questions are referenced by other questions (original query that started the GetZoneData
+ // question) through their "nta" pointer. Normally when the original query stops, it stops the
+ // GetZoneData question and also frees the memory (See CancelGetZoneData). If we stop the GetZoneData
+ // question followed by the original query that refers to this GetZoneData question, we will end up
+ // freeing the GetZoneData question and then start the "freed" question at the end.
+
+ if (IsGetZoneDataQuestion(q))
+ {
+ DNSQuestion *refq = q->next;
+ LogInfo("mDNSCoreRestartAddressQueries: Skipping GetZoneDataQuestion %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype));
+ // debug stuff, we just try to find the referencing question and don't do much with it
+ while (refq)
+ {
+ if (q == &refq->nta->question)
+ {
+ LogInfo("mDNSCoreRestartAddressQueries: Question %p %##s (%s) referring to GetZoneDataQuestion %p, not stopping", refq, refq->qname.c, DNSTypeName(refq->qtype), q);
+ }
+ refq = refq->next;
+ }
+ continue;
+ }
+
+ // This function is called when /etc/hosts changes and that could affect A, AAAA and CNAME queries
+ if (q->qtype != kDNSType_A && q->qtype != kDNSType_AAAA && q->qtype != kDNSType_CNAME) continue;
+
+ // If the search domains did not change, then we restart all the queries. Otherwise, only
+ // for queries for which we "might" have appended search domains ("might" because we may
+ // find results before we apply search domains even though AppendSearchDomains is set to 1)
+ if (!SearchDomainsChanged || q->AppendSearchDomains)
+ {
+ // NOTE: CacheRecordRmvEventsForQuestion will not generate RMV events for queries that have non-zero
+ // LOAddressAnswers. Hence it is important that we call CacheRecordRmvEventsForQuestion before
+ // LocalRecordRmvEventsForQuestion (which decrements LOAddressAnswers). Let us say that
+ // /etc/hosts has an A Record for web.apple.com. Any queries for web.apple.com will be answered locally.
+ // But this can't prevent a CNAME/AAAA query to not to be sent on the wire. When it is sent on the wire,
+ // it could create cache entries. When we are restarting queries, we can't deliver the cache RMV events
+ // for the original query using these cache entries as ADDs were never delivered using these cache
+ // entries and hence this order is needed.
+
+ // If the query is suppressed, the RMV events won't be delivered
+ if (!CacheRecordRmvEventsForQuestion(m, q)) { LogInfo("mDNSCoreRestartAddressQueries: Question deleted while delivering Cache Record RMV events"); continue; }
+
+ // SuppressQuery status does not affect questions that are answered using local records
+ if (!LocalRecordRmvEventsForQuestion(m, q)) { LogInfo("mDNSCoreRestartAddressQueries: Question deleted while delivering Local Record RMV events"); continue; }
+
+ LogInfo("mDNSCoreRestartAddressQueries: Stop question %p %##s (%s), AppendSearchDomains %d, qnameOrig %p", q,
+ q->qname.c, DNSTypeName(q->qtype), q->AppendSearchDomains, q->qnameOrig);
+ mDNS_StopQuery_internal(m, q);
+ // Reset state so that it looks like it was in the beginning i.e it should look at /etc/hosts, cache
+ // and then search domains should be appended. At the beginning, qnameOrig was NULL.
+ if (q->qnameOrig)
+ {
+ LogInfo("mDNSCoreRestartAddressQueries: qnameOrig %##s", q->qnameOrig);
+ AssignDomainName(&q->qname, q->qnameOrig);
+ mDNSPlatformMemFree(q->qnameOrig);
+ q->qnameOrig = mDNSNULL;
+ q->RetryWithSearchDomains = ApplySearchDomainsFirst(q) ? 1 : 0;
+ }
+ q->SearchListIndex = 0;
+ q->next = restart;
+ restart = q;
+ }
+ }
+
+ // 3. Callback before we start the query
+ if (BeforeStartCallback) BeforeStartCallback(m, context);
+
+ // 4. Restart all the stopped queries
+ while (restart)
+ {
+ q = restart;
+ restart = restart->next;
+ q->next = mDNSNULL;
+ LogInfo("mDNSCoreRestartAddressQueries: Start question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype));
+ mDNS_StartQuery_internal(m, q);
+ }
+ }
+
mDNSexport void mDNSCoreRestartQueries(mDNS *const m)
{
DNSQuestion *q;
{
#ifndef IDLESLEEPCONTROL_DISABLED
mDNSBool allowSleep = mDNStrue;
+ char reason[128];
+
+ reason[0] = 0;
if (m->SystemSleepOnlyIfWakeOnLAN)
{
if (m->ProxyRecords)
{
allowSleep = mDNSfalse;
+ mDNS_snprintf(reason, sizeof(reason), "sleep proxy for %d records", m->ProxyRecords);
LogInfo("Sleep disabled because we are proxying %d records", m->ProxyRecords);
}
NetworkInterfaceInfo *intf;
for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next))
{
- if (intf->McastTxRx)
+ if (intf->McastTxRx && !intf->Loopback)
{
// Disallow sleep if this interface doesn't support NetWake
if (!intf->NetWake)
{
allowSleep = mDNSfalse;
+ mDNS_snprintf(reason, sizeof(reason), "%s does not support NetWake", intf->ifname);
LogInfo("Sleep disabled because %s does not support NetWake", intf->ifname);
break;
}
if (FindSPSInCache1(m, &intf->NetWakeBrowse, mDNSNULL, mDNSNULL) == mDNSNULL)
{
allowSleep = mDNSfalse;
+ mDNS_snprintf(reason, sizeof(reason), "%s does not support NetWake", intf->ifname);
LogInfo("Sleep disabled because %s has no sleep proxy", intf->ifname);
break;
}
}
}
}
-#endif /* !defined(IDLESLEEPCONTROL_DISABLED) */
}
// Call the platform code to enable/disable sleep
- mDNSPlatformSetAllowSleep(m, allowSleep);
+ mDNSPlatformSetAllowSleep(m, allowSleep, reason);
+#endif /* !defined(IDLESLEEPCONTROL_DISABLED) */
}
-mDNSlocal void SendSPSRegistration(mDNS *const m, NetworkInterfaceInfo *intf, const mDNSOpaque16 id)
+mDNSlocal void SendSPSRegistrationForOwner(mDNS *const m, NetworkInterfaceInfo *const intf, const mDNSOpaque16 id, const OwnerOptData *const owner)
{
const int optspace = DNSOpt_Header_Space + DNSOpt_LeaseData_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC);
const int sps = intf->NextSPSAttempt / 3;
for (rr = m->ResourceRecords; rr; rr=rr->next)
if (rr->resrec.RecordType > kDNSRecordTypeDeregistering)
if (rr->resrec.InterfaceID == intf->InterfaceID || (!rr->resrec.InterfaceID && (rr->ForceMCast || IsLocalDomain(rr->resrec.name))))
- rr->SendRNow = mDNSInterfaceMark; // mark it now
+ if (mDNSPlatformMemSame(owner, &rr->WakeUp, sizeof(*owner)))
+ rr->SendRNow = mDNSInterfaceMark; // mark it now
while (1)
{
for (rr = m->ResourceRecords; rr; rr=rr->next)
if (rr->SendRNow || (!mDNSOpaque16IsZero(id) && !AuthRecord_uDNS(rr) && mDNSSameOpaque16(rr->updateid, id) && m->timenow - (rr->LastAPTime + rr->ThisAPInterval) >= 0))
- {
- mDNSu8 *newptr;
- const mDNSu8 *const limit = m->omsg.data + (m->omsg.h.mDNS_numUpdates ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData) - optspace;
- if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
- rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the 'unique' bit so PutResourceRecord will set it
- newptr = PutResourceRecordTTLWithLimit(&m->omsg, p, &m->omsg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl, limit);
- rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear 'unique' bit back to normal state
- if (!newptr)
- LogSPS("SendSPSRegistration put %s FAILED %d/%d %s", intf->ifname, p - m->omsg.data, limit - m->omsg.data, ARDisplayString(m, rr));
- else
+ if (mDNSPlatformMemSame(owner, &rr->WakeUp, sizeof(*owner)))
{
- LogSPS("SendSPSRegistration put %s %s", intf->ifname, ARDisplayString(m, rr));
- rr->SendRNow = mDNSNULL;
- rr->ThisAPInterval = mDNSPlatformOneSecond;
- rr->LastAPTime = m->timenow;
- rr->updateid = m->omsg.h.id;
- if (m->NextScheduledResponse - (rr->LastAPTime + rr->ThisAPInterval) >= 0)
- m->NextScheduledResponse = (rr->LastAPTime + rr->ThisAPInterval);
- p = newptr;
+ mDNSu8 *newptr;
+ const mDNSu8 *const limit = m->omsg.data + (m->omsg.h.mDNS_numUpdates ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData) - optspace;
+ if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
+ rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the 'unique' bit so PutResourceRecord will set it
+ newptr = PutResourceRecordTTLWithLimit(&m->omsg, p, &m->omsg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl, limit);
+ rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear 'unique' bit back to normal state
+ if (!newptr)
+ LogSPS("SendSPSRegistration put %s FAILED %d/%d %s", intf->ifname, p - m->omsg.data, limit - m->omsg.data, ARDisplayString(m, rr));
+ else
+ {
+ LogSPS("SendSPSRegistration put %s %s", intf->ifname, ARDisplayString(m, rr));
+ rr->SendRNow = mDNSNULL;
+ rr->ThisAPInterval = mDNSPlatformOneSecond;
+ rr->LastAPTime = m->timenow;
+ rr->updateid = m->omsg.h.id;
+ if (m->NextScheduledResponse - (rr->LastAPTime + rr->ThisAPInterval) >= 0)
+ m->NextScheduledResponse = (rr->LastAPTime + rr->ThisAPInterval);
+ p = newptr;
+ }
}
- }
if (!m->omsg.h.mDNS_numUpdates) break;
else
{
AuthRecord opt;
- mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
+ mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
opt.resrec.rrclass = NormalMaxDNSMessageData;
opt.resrec.rdlength = sizeof(rdataOPT) * 2; // Two options in this OPT record
opt.resrec.rdestimate = sizeof(rdataOPT) * 2;
- opt.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease;
- opt.resrec.rdata->u.opt[0].optlen = DNSOpt_LeaseData_Space - 4;
- opt.resrec.rdata->u.opt[0].u.updatelease = DEFAULT_UPDATE_LEASE;
- SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[1]);
+ opt.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease;
+ opt.resrec.rdata->u.opt[0].optlen = DNSOpt_LeaseData_Space - 4;
+ opt.resrec.rdata->u.opt[0].u.updatelease = DEFAULT_UPDATE_LEASE;
+ if (!owner->HMAC.l[0]) // If no owner data,
+ SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[1]); // use our own interface information
+ else // otherwise, use the owner data we were given
+ {
+ opt.resrec.rdata->u.opt[1].u.owner = *owner;
+ opt.resrec.rdata->u.opt[1].opt = kDNSOpt_Owner;
+ opt.resrec.rdata->u.opt[1].optlen = DNSOpt_Owner_Space(&owner->HMAC, &owner->IMAC) - 4;
+ }
LogSPS("SendSPSRegistration put %s %s", intf->ifname, ARDisplayString(m, &opt));
p = PutResourceRecordTTLWithLimit(&m->omsg, p, &m->omsg.h.numAdditionals, &opt.resrec, opt.resrec.rroriginalttl, m->omsg.data + AbsoluteMaxDNSMessageData);
if (!p)
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 (mDNSOpaque16IsZero(id) && intf->NextSPSAttempt < 8) intf->NextSPSAttempt++;
}
+mDNSlocal mDNSBool RecordIsFirstOccurrenceOfOwner(mDNS *const m, const AuthRecord *const rr)
+ {
+ AuthRecord *ar;
+ for (ar = m->ResourceRecords; ar && ar != rr; ar=ar->next)
+ if (mDNSPlatformMemSame(&rr->WakeUp, &ar->WakeUp, sizeof(rr->WakeUp))) return mDNSfalse;
+ return mDNStrue;
+ }
+
+mDNSlocal void SendSPSRegistration(mDNS *const m, NetworkInterfaceInfo *const intf, const mDNSOpaque16 id)
+ {
+ AuthRecord *ar;
+ OwnerOptData owner = zeroOwner;
+
+ SendSPSRegistrationForOwner(m, intf, id, &owner);
+
+ for (ar = m->ResourceRecords; ar; ar=ar->next)
+ {
+ if (!mDNSPlatformMemSame(&owner, &ar->WakeUp, sizeof(owner)) && RecordIsFirstOccurrenceOfOwner(m, ar))
+ {
+ owner = ar->WakeUp;
+ SendSPSRegistrationForOwner(m, intf, id, &owner);
+ }
+ }
+ }
+
// RetrySPSRegistrations is called from SendResponses, with the lock held
mDNSlocal void RetrySPSRegistrations(mDNS *const m)
{
{
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.,
// If we'd previously verified this record, put it back to probing state and try again
if (rr->resrec.RecordType == kDNSRecordTypeVerified)
{
- LogMsg("mDNSCoreReceiveResponse: Reseting to Probing: %s", ARDisplayString(m, rr));
+ LogMsg("mDNSCoreReceiveResponse: Resetting to Probing: %s", ARDisplayString(m, rr));
rr->resrec.RecordType = kDNSRecordTypeUnique;
// We set ProbeCount to one more than the usual value so we know we've already touched this record.
// This is because our single probe for "example-name.local" could yield a response with (say) two A records and
// If we're probing for this record, we just failed
else if (rr->resrec.RecordType == kDNSRecordTypeUnique)
{
- LogMsg("mDNSCoreReceiveResponse: ProbeCount %d; will rename %s", rr->ProbeCount, ARDisplayString(m, rr));
+ LogMsg("mDNSCoreReceiveResponse: ProbeCount %d; will deregister %s", rr->ProbeCount, ARDisplayString(m, rr));
mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict);
}
// We assumed this record must be unique, but we were wrong. (e.g. There are two mDNSResponders on the
ptr = getQuestion(response, ptr, end, InterfaceID, &q);
if (ptr && (qptr = ExpectingUnicastResponseForQuestion(m, dstport, response->h.id, &q, !dstaddr)))
{
+ CacheRecord *rr, *neg = mDNSNULL;
+ mDNSu32 slot = HashSlot(&q.qname);
+ CacheGroup *cg = CacheGroupForName(m, slot, q.qnamehash, &q.qname);
+ for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
+ if (SameNameRecordAnswersQuestion(&rr->resrec, qptr))
+ {
+ // 1. If we got a fresh answer to this query, then don't need to generate a negative entry
+ if (RRExpireTime(rr) - m->timenow > 0) break;
+ // 2. If we already had a negative entry, keep track of it so we can resurrect it instead of creating a new one
+ if (rr->resrec.RecordType == kDNSRecordTypePacketNegative) neg = rr;
+ }
// When we're doing parallel unicast and multicast queries for dot-local names (for supporting Microsoft
// Active Directory sites) we don't want to waste memory making negative cache entries for all the unicast answers.
// Otherwise we just fill up our cache with negative entries for just about every single multicast name we ever look up
// in conflict with the mDNS spec, because that spec says, "Multicast DNS Zones have no SOA record," so it's okay to cache
// negative answers for "local. SOA" from a uDNS server, because the mDNS spec already says that such records do not exist :-)
if (!InterfaceID && q.qtype != kDNSType_SOA && IsLocalDomain(&q.qname))
- LogInfo("Skipping check to see if we need to generate a negative cache entry for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
+ {
+ // If we did not find a positive answer and we can append search domains to this question,
+ // generate a negative response (without creating a cache entry) to append search domains.
+ if (qptr->AppendSearchDomains && !rr)
+ {
+ LogInfo("mDNSCoreReceiveResponse: Generate negative response for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
+ m->CurrentQuestion = qptr;
+ GenerateNegativeResponse(m);
+ m->CurrentQuestion = mDNSNULL;
+ }
+ else LogInfo("mDNSCoreReceiveResponse: Skipping check to see if we need to generate a negative cache entry for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
+ }
else
{
- CacheRecord *rr, *neg = mDNSNULL;
- mDNSu32 slot = HashSlot(&q.qname);
- CacheGroup *cg = CacheGroupForName(m, slot, q.qnamehash, &q.qname);
- for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
- if (SameNameRecordAnswersQuestion(&rr->resrec, qptr))
- {
- // 1. If we got a fresh answer to this query, then don't need to generate a negative entry
- if (RRExpireTime(rr) - m->timenow > 0) break;
- // 2. If we already had a negative entry, keep track of it so we can resurrect it instead of creating a new one
- if (rr->resrec.RecordType == kDNSRecordTypePacketNegative) neg = rr;
- }
-
if (!rr)
{
// We start off assuming a negative caching TTL of 60 seconds
}
}
+// ScheduleWakeup causes all proxy records with WakeUp.HMAC matching mDNSEthAddr 'e' to be deregistered, causing
+// multiple wakeup magic packets to be sent if appropriate, and all records to be ultimately freed after a few seconds.
+// ScheduleWakeup is called on mDNS record conflicts, ARP conflicts, NDP conflicts, or reception of trigger traffic
+// that warrants waking the sleeping host.
+// ScheduleWakeup must be called with the lock held (ScheduleWakeupForList uses mDNS_Deregister_internal)
+
mDNSlocal void ScheduleWakeupForList(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *e, AuthRecord *const thelist)
{
+ // We don't need to use the m->CurrentRecord mechanism here because the target HMAC is nonzero,
+ // so all we're doing is marking the record to generate a few wakeup packets
AuthRecord *rr;
- for (rr = thelist; rr; rr=rr->next)
+ if (!e->l[0]) { LogMsg("ScheduleWakeupForList ERROR: Target HMAC is zero"); return; }
+ for (rr = thelist; rr; rr = rr->next)
if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering && mDNSSameEthAddress(&rr->WakeUp.HMAC, e))
+ {
+ LogInfo("ScheduleWakeupForList: Scheduling wakeup packets for %s", ARDisplayString(m, rr));
mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
+ }
}
mDNSlocal void ScheduleWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *e)
{
+ if (!e->l[0]) { LogMsg("ScheduleWakeup ERROR: Target HMAC is zero"); return; }
ScheduleWakeupForList(m, InterfaceID, e, m->DuplicateRecords);
ScheduleWakeupForList(m, InterfaceID, e, m->ResourceRecords);
}
if (result == mStatus_NameConflict)
{
- LogMsg("Received Conflicting mDNS -- waking %s %.6a %s", InterfaceNameForID(m, ar->resrec.InterfaceID), &ar->WakeUp.HMAC, ARDisplayString(m, ar));
- SendWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.IMAC, &ar->WakeUp.password);
- ScheduleWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.HMAC);
+ mDNS_Lock(m);
+ LogMsg("%-7s Conflicting mDNS -- waking %.6a %s", InterfaceNameForID(m, ar->resrec.InterfaceID), &ar->WakeUp.HMAC, ARDisplayString(m, ar));
+ if (ar->WakeUp.HMAC.l[0])
+ {
+ SendWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.IMAC, &ar->WakeUp.password); // Send one wakeup magic packet
+ ScheduleWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.HMAC); // Schedule all other records with the same owner to be woken
+ }
+ mDNS_Unlock(m);
}
- else if (result == mStatus_MemFree)
+
+ if (result == mStatus_NameConflict || result == mStatus_MemFree)
{
m->ProxyRecords--;
mDNSPlatformMemFree(ar);
m->rec.r.resrec.rrclass &= ~kDNSClass_UniqueRRSet;
ClearIdenticalProxyRecords(m, &owner, m->DuplicateRecords); // Make sure we don't have any old stale duplicates of this record
ClearIdenticalProxyRecords(m, &owner, m->ResourceRecords);
- mDNS_SetupResourceRecord(ar, mDNSNULL, InterfaceID, m->rec.r.resrec.rrtype, m->rec.r.resrec.rroriginalttl, RecordType, SPSRecordCallback, ar);
+ mDNS_SetupResourceRecord(ar, mDNSNULL, InterfaceID, m->rec.r.resrec.rrtype, m->rec.r.resrec.rroriginalttl, RecordType, AuthRecordAny, SPSRecordCallback, ar);
AssignDomainName(&ar->namestorage, m->rec.r.resrec.name);
ar->resrec.rdlength = GetRDLength(&m->rec.r.resrec, mDNSfalse);
ar->resrec.rdata->MaxRDLength = RDLengthMem;
}
else
{
- mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
+ mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
opt.resrec.rrclass = NormalMaxDNSMessageData;
opt.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record
opt.resrec.rdestimate = sizeof(rdataOPT);
{
if (InterfaceID)
{
- AuthRecord *rr;
mDNSu32 updatelease = 60 * 60; // If SPS fails to indicate lease time, assume one hour
const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space);
if (ptr)
m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
}
- for (rr = m->ResourceRecords; rr; rr=rr->next)
+ if (m->CurrentRecord)
+ LogMsg("mDNSCoreReceiveUpdateR ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
+ m->CurrentRecord = m->ResourceRecords;
+ while (m->CurrentRecord)
+ {
+ AuthRecord *const rr = m->CurrentRecord;
if (rr->resrec.InterfaceID == InterfaceID || (!rr->resrec.InterfaceID && (rr->ForceMCast || IsLocalDomain(rr->resrec.name))))
if (mDNSSameOpaque16(rr->updateid, msg->h.id))
{
rr->updateid = zeroID;
rr->expire = NonZeroTime(m->timenow + updatelease * mDNSPlatformOneSecond);
- LogSPS("Sleep Proxy registered record %5d %s", updatelease, ARDisplayString(m,rr));
+ LogSPS("Sleep Proxy %s record %5d %s", rr->WakeUp.HMAC.l[0] ? "transferred" : "registered", updatelease, ARDisplayString(m,rr));
+ if (rr->WakeUp.HMAC.l[0])
+ {
+ rr->WakeUp.HMAC = zeroEthAddr; // Clear HMAC so that mDNS_Deregister_internal doesn't waste packets trying to wake this host
+ rr->RequireGoodbye = mDNSfalse; // and we don't want to send goodbye for it
+ mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
+ }
}
-
+ // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because
+ // new records could have been added to the end of the list as a result of that call.
+ if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now
+ m->CurrentRecord = rr->next;
+ }
}
// If we were waiting to go to sleep, then this SPS registration or wide-area record deletion
// may have been the thing we were waiting for, so schedule another check to see if we can sleep now.
mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, DNSQuestion *const question)
{
DNSQuestion *q;
+ DNSQuestion *first = mDNSNULL;
+
+ // This is referring to some other question as duplicate. No other question can refer to this
+ // question as a duplicate.
+ if (question->DuplicateOf)
+ {
+ LogInfo("UpdateQuestionDuplicates: question %p %##s (%s) duplicate of %p %##s (%s)",
+ question, question->qname.c, DNSTypeName(question->qtype),
+ question->DuplicateOf, question->DuplicateOf->qname.c, DNSTypeName(question->DuplicateOf->qtype));
+ return;
+ }
+
for (q = m->Questions; q; q=q->next) // Scan our list of questions
if (q->DuplicateOf == question) // To see if any questions were referencing this as their duplicate
- if ((q->DuplicateOf = FindDuplicateQuestion(m, q)) == mDNSNULL)
+ {
+ q->DuplicateOf = first;
+ if (!first)
{
+ first = q;
// If q used to be a duplicate, but now is not,
// then inherit the state from the question that's going away
q->LastQTime = question->LastQTime;
SetNextQueryTime(m,q);
}
+ }
+ }
+
+mDNSexport McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, mDNSu32 timeout)
+ {
+ McastResolver **p = &m->McastResolvers;
+ McastResolver *tmp = mDNSNULL;
+
+ if (!d) d = (const domainname *)"";
+
+ LogInfo("mDNS_AddMcastResolver: Adding %##s, InterfaceID %p, timeout %u", d->c, interface, timeout);
+
+ if (m->mDNS_busy != m->mDNS_reentrancy+1)
+ LogMsg("mDNS_AddMcastResolver: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+
+ while (*p) // Check if we already have this {interface, domain} tuple registered
+ {
+ if ((*p)->interface == interface && SameDomainName(&(*p)->domain, d))
+ {
+ if (!((*p)->flags & DNSServer_FlagDelete)) LogMsg("Note: Mcast Resolver domain %##s (%p) registered more than once", d->c, interface);
+ (*p)->flags &= ~DNSServer_FlagDelete;
+ tmp = *p;
+ *p = tmp->next;
+ tmp->next = mDNSNULL;
+ }
+ else
+ p=&(*p)->next;
+ }
+
+ if (tmp) *p = tmp; // move to end of list, to ensure ordering from platform layer
+ else
+ {
+ // allocate, add to list
+ *p = mDNSPlatformMemAllocate(sizeof(**p));
+ if (!*p) LogMsg("mDNS_AddMcastResolver: ERROR!! - malloc");
+ else
+ {
+ (*p)->interface = interface;
+ (*p)->flags = DNSServer_FlagNew;
+ (*p)->timeout = timeout;
+ AssignDomainName(&(*p)->domain, d);
+ (*p)->next = mDNSNULL;
+ }
+ }
+ return(*p);
}
mDNSinline mDNSs32 PenaltyTimeForServer(mDNS *m, DNSServer *server)
return -1;
}
+// Normally, we have McastResolvers for .local, in-addr.arpa and ip6.arpa. But there
+// can be queries that can forced to multicast (ForceMCast) even though they don't end in these
+// names. In that case, we give a default timeout of 5 seconds
+#define DEFAULT_MCAST_TIMEOUT 5
+mDNSlocal mDNSu32 GetTimeoutForMcastQuestion(mDNS *m, DNSQuestion *question)
+ {
+ McastResolver *curmatch = mDNSNULL;
+ int bestmatchlen = -1, namecount = CountLabels(&question->qname);
+ McastResolver *curr;
+ int bettermatch, currcount;
+ for (curr = m->McastResolvers; curr; curr = curr->next)
+ {
+ currcount = CountLabels(&curr->domain);
+ bettermatch = BetterMatchForName(&question->qname, namecount, &curr->domain, currcount, bestmatchlen);
+ // Take the first best match. If there are multiple equally good matches (bettermatch = 0), we take
+ // the timeout value from the first one
+ if (bettermatch == 1)
+ {
+ curmatch = curr;
+ bestmatchlen = currcount;
+ }
+ }
+ LogInfo("GetTimeoutForMcastQuestion: question %##s curmatch %p, Timeout %d", question->qname.c, curmatch,
+ curmatch ? curmatch->timeout : DEFAULT_MCAST_TIMEOUT);
+ return ( curmatch ? curmatch->timeout : DEFAULT_MCAST_TIMEOUT);
+ }
+
// Sets all the Valid DNS servers for a question
-mDNSexport void SetValidDNSServers(mDNS *m, DNSQuestion *question)
+mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question)
{
DNSServer *curmatch = mDNSNULL;
int bestmatchlen = -1, namecount = CountLabels(&question->qname);
DNSServer *curr;
int bettermatch, currcount;
int index = 0;
+ mDNSu32 timeout = 0;
question->validDNSServers = zeroOpaque64;
for (curr = m->DNSServers; curr; curr = curr->next)
if (curr->flags & DNSServer_FlagDelete)
{ debugf("SetValidDNSServers: Delete set for index %d, DNS server %#a (Domain %##s), scoped %d", index, &curr->addr, curr->domain.c, curr->scoped); continue; }
+ // This happens normally when you unplug the interface where we reset the interfaceID to mDNSInterface_Any for all
+ // the DNS servers whose scope match the interfaceID. Few seconds later, we also receive the updated DNS configuration.
+ // But any questions that has mDNSInterface_Any scope that are started/restarted before we receive the update
+ // (e.g., CheckSuppressUnusableQuestions is called when interfaces are deregistered with the core) should not
+ // match the scoped entries by mistake.
+ //
+ // Note: DNS configuration change will help pick the new dns servers but currently it does not affect the timeout
+
+ if (curr->scoped && curr->interface == mDNSInterface_Any)
+ { debugf("SetValidDNSServers: Scoped DNS server %#a (Domain %##s) with Interface Any", &curr->addr, curr->domain.c); continue; }
+
currcount = CountLabels(&curr->domain);
if ((!curr->scoped && (!question->InterfaceID || (question->InterfaceID == mDNSInterface_Unicast))) || (curr->interface == question->InterfaceID))
{
{
curmatch = curr;
bestmatchlen = currcount;
- if (bettermatch) { debugf("SetValidDNSServers: Resetting all the bits"); question->validDNSServers = zeroOpaque64; }
- debugf("SetValidDNSServers: Setting the bit for DNS server Address %#a (Domain %##s), Scoped:%d index %d", &curr->addr, curr->domain.c, curr->scoped, index);
+ if (bettermatch) { debugf("SetValidDNSServers: Resetting all the bits"); question->validDNSServers = zeroOpaque64; timeout = 0; }
+ debugf("SetValidDNSServers: question %##s Setting the bit for DNS server Address %#a (Domain %##s), Scoped:%d index %d,"
+ " Timeout %d, interface %p", question->qname.c, &curr->addr, curr->domain.c, curr->scoped, index, curr->timeout,
+ curr->interface);
+ timeout += curr->timeout;
bit_set_opaque64(question->validDNSServers, index);
}
}
index++;
}
question->noServerResponse = 0;
+
debugf("SetValidDNSServers: ValidDNSServer bits 0x%x%x for question %p %##s (%s)",
question->validDNSServers.l[1], question->validDNSServers.l[0], question, question->qname.c, DNSTypeName(question->qtype));
+ // If there are no matching resolvers, then use the default value to timeout
+ return (timeout ? timeout : DEFAULT_UDNS_TIMEOUT);
}
// Get the Best server that matches a name. If you find penalized servers, look for the one
return mDNStrue;
}
-mDNSlocal void CheckSuppressedCurrentQuestion(mDNS *const m, DNSQuestion *q)
+mDNSlocal void CacheRecordRmvEventsForCurrentQuestion(mDNS *const m, DNSQuestion *q)
{
CacheRecord *rr;
mDNSu32 slot;
CacheGroup *cg;
- // Temporarily turn off suppression so that AnswerCurrentQuestionWithResourceRecord
- // can answer the question
- q->SuppressQuery = mDNSfalse;
slot = HashSlot(&q->qname);
cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
// Don't deliver RMV events for negative records
if (rr->resrec.RecordType == kDNSRecordTypePacketNegative)
{
- LogInfo("CheckSuppressedCurrentQuestion: CacheRecord %s Suppressing RMV events for question %p %##s (%s), CRActiveQuestion %p, CurrentAnswers %d",
+ LogInfo("CacheRecordRmvEventsForCurrentQuestion: CacheRecord %s Suppressing RMV events for question %p %##s (%s), CRActiveQuestion %p, CurrentAnswers %d",
CRDisplayString(m, rr), q, q->qname.c, DNSTypeName(q->qtype), rr->CRActiveQuestion, q->CurrentAnswers);
continue;
}
if (SameNameRecordAnswersQuestion(&rr->resrec, q))
{
- LogInfo("CheckSuppressedCurrentQuestion: Calling AnswerCurrentQuestionWithResourceRecord (RMV) for question %##s using resource record %s",
- q->qname.c, CRDisplayString(m, rr));
+ LogInfo("CacheRecordRmvEventsForCurrentQuestion: Calling AnswerCurrentQuestionWithResourceRecord (RMV) for question %##s using resource record %s LocalAnswers %d",
+ q->qname.c, CRDisplayString(m, rr), q->LOAddressAnswers);
q->CurrentAnswers--;
if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--;
// when the cache entry is about to expire, we won't find an active question
// (pointed by CRActiveQuestion) to refresh the cache.
for (qptr = m->Questions; qptr; qptr=qptr->next)
- if (ActiveQuestion(qptr) && ResourceRecordAnswersQuestion(&rr->resrec, qptr))
+ if (qptr != q && ActiveQuestion(qptr) && ResourceRecordAnswersQuestion(&rr->resrec, qptr))
break;
if (qptr)
- LogInfo("CheckSuppressedCurrentQuestion: Updating CRActiveQuestion to %p for cache record %s, "
+ LogInfo("CacheRecordRmvEventsForCurrentQuestion: Updating CRActiveQuestion to %p for cache record %s, "
"Original question CurrentAnswers %d, new question CurrentAnswers %d, SuppressUnusable %d, SuppressQuery %d",
qptr, CRDisplayString(m,rr), q->CurrentAnswers, qptr->CurrentAnswers, qptr->SuppressUnusable, qptr->SuppressQuery);
if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here
}
}
- if (m->CurrentQuestion == q) q->SuppressQuery = mDNStrue;
}
mDNSlocal mDNSBool IsQuestionNew(mDNS *const m, DNSQuestion *question)
return mDNSfalse;
}
+mDNSlocal mDNSBool LocalRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q)
+ {
+ AuthRecord *rr;
+ mDNSu32 slot;
+ AuthGroup *ag;
+
+ if (m->CurrentQuestion)
+ LogMsg("LocalRecordRmvEventsForQuestion: ERROR m->CurrentQuestion already set: %##s (%s)",
+ m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
+
+ if (IsQuestionNew(m, q))
+ {
+ LogInfo("LocalRecordRmvEventsForQuestion: New Question %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+ return mDNStrue;
+ }
+ m->CurrentQuestion = q;
+ slot = AuthHashSlot(&q->qname);
+ ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname);
+ if (ag)
+ {
+ for (rr = ag->members; rr; rr=rr->next)
+ // Filter the /etc/hosts records - LocalOnly, Unique, A/AAAA/CNAME
+ if (LORecordAnswersAddressType(rr) && LocalOnlyRecordAnswersQuestion(rr, q))
+ {
+ LogInfo("LocalRecordRmvEventsForQuestion: Delivering possible Rmv events with record %s",
+ ARDisplayString(m, rr));
+ if (q->CurrentAnswers <= 0 || q->LOAddressAnswers <= 0)
+ {
+ LogMsg("LocalRecordRmvEventsForQuestion: ERROR!! CurrentAnswers or LOAddressAnswers is zero %p %##s"
+ " (%s) CurrentAnswers %d, LOAddressAnswers %d", q, q->qname.c, DNSTypeName(q->qtype),
+ q->CurrentAnswers, q->LOAddressAnswers);
+ continue;
+ }
+ AnswerLocalQuestionWithLocalAuthRecord(m, rr, QC_rmv); // MUST NOT dereference q again
+ if (m->CurrentQuestion != q) { m->CurrentQuestion = mDNSNULL; return mDNSfalse; }
+ }
+ }
+ m->CurrentQuestion = mDNSNULL;
+ return mDNStrue;
+ }
+
+// Returns false if the question got deleted while delivering the RMV events
+// The caller should handle the case
+mDNSlocal mDNSBool CacheRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q)
+ {
+ if (m->CurrentQuestion)
+ LogMsg("CacheRecordRmvEventsForQuestion: ERROR m->CurrentQuestion already set: %##s (%s)",
+ m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
+
+ // If it is a new question, we have not delivered any ADD events yet. So, don't deliver RMV events.
+ // If this question was answered using local auth records, then you can't deliver RMVs using cache
+ if (!IsQuestionNew(m, q) && !q->LOAddressAnswers)
+ {
+ m->CurrentQuestion = q;
+ CacheRecordRmvEventsForCurrentQuestion(m, q);
+ if (m->CurrentQuestion != q) { m->CurrentQuestion = mDNSNULL; return mDNSfalse; }
+ m->CurrentQuestion = mDNSNULL;
+ }
+ else { LogInfo("CacheRecordRmvEventsForQuestion: Question %p %##s (%s) is a new question", q, q->qname.c, DNSTypeName(q->qtype)); }
+ return mDNStrue;
+ }
+
// The caller should hold the lock
mDNSexport void CheckSuppressUnusableQuestions(mDNS *const m)
{
- DNSQuestion *q, *qnext;
+ DNSQuestion *q;
DNSQuestion *restart = mDNSNULL;
// We look through all questions including new questions. During network change events,
// which may be suppressed at this instance. Before it is handled we get another network
// event that changes the status e.g., address becomes available. If we did not process
// new questions, we would never change its SuppressQuery status.
- for (q = m->Questions; q ; q = qnext)
- {
- qnext = q->next;
+ //
+ // CurrentQuestion is used by RmvEventsForQuestion below. While delivering RMV events, the
+ // application callback can potentially stop the current question (detected by CurrentQuestion) or
+ // *any* other question which could be the next one that we may process here. RestartQuestion
+ // points to the "next" question which will be automatically advanced in mDNS_StopQuery_internal
+ // if the "next" question is stopped while the CurrentQuestion is stopped
+ if (m->RestartQuestion)
+ LogMsg("CheckSuppressUnusableQuestions: ERROR!! m->RestartQuestion already set: %##s (%s)",
+ m->RestartQuestion->qname.c, DNSTypeName(m->RestartQuestion->qtype));
+ m->RestartQuestion = m->Questions;
+ while (m->RestartQuestion)
+ {
+ q = m->RestartQuestion;
+ m->RestartQuestion = q->next;
if (!mDNSOpaque16IsZero(q->TargetQID) && q->SuppressUnusable)
{
mDNSBool old = q->SuppressQuery;
q->SuppressQuery = ShouldSuppressQuery(m, &q->qname, q->qtype, q->InterfaceID);
if (q->SuppressQuery != old)
{
- if (q->SuppressQuery)
- {
- // Previously it was not suppressed, Generate RMV events for the ADDs that we might have delivered before
- // followed by a negative cache response
- if (m->CurrentQuestion)
- LogMsg("CheckSuppressUnusableQuestions: ERROR m->CurrentQuestion already set: %##s (%s)",
- m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
-
- // If it is a new question, we have not delivered any ADD events yet. So, don't deliver RMV events.
- if (!IsQuestionNew(m, q))
- {
- m->CurrentQuestion = q;
- CheckSuppressedCurrentQuestion(m, q);
- if (m->CurrentQuestion != q)
- {
- m->CurrentQuestion = mDNSNULL;
- LogInfo("CheckSuppressUnusableQuestions: Question deleted while giving RMV events");
- continue;
- }
- m->CurrentQuestion = mDNSNULL;
- }
- else { debugf("CheckSuppressUnusableQuestion: Question %p %##s (%s) is a new question", q, q->qname.c, DNSTypeName(q->qtype)); }
- }
+ // NOTE: CacheRecordRmvEventsForQuestion will not generate RMV events for queries that have non-zero
+ // LOddressAnswers. Hence it is important that we call CacheRecordRmvEventsForQuestion before
+ // LocalRecordRmvEventsForQuestion (which decrements LOAddressAnswers)
+
+ if (q->SuppressQuery)
+ {
+ // Previously it was not suppressed, Generate RMV events for the ADDs that we might have delivered before
+ // followed by a negative cache response. Temporarily turn off suppression so that
+ // AnswerCurrentQuestionWithResourceRecord can answer the question
+ q->SuppressQuery = mDNSfalse;
+ if (!CacheRecordRmvEventsForQuestion(m, q)) { LogInfo("CheckSuppressUnusableQuestions: Question deleted while delivering RMV events"); continue; }
+ q->SuppressQuery = mDNStrue;
+ }
+
+ // SuppressUnusable does not affect questions that are answered from the local records (/etc/hosts)
+ // and SuppressQuery status does not mean anything for these questions. As we are going to stop the
+ // question below, we need to deliver the RMV events so that the ADDs that will be delivered during
+ // the restart will not be a duplicate ADD
+ if (!LocalRecordRmvEventsForQuestion(m, q)) { LogInfo("CheckSuppressUnusableQuestions: Question deleted while delivering RMV events"); continue; }
// There are two cases here.
//
// is a duplicate of non-SuppressUnusable question if it is not suppressed (SuppressQuery is false).
// A SuppressUnusable question is not a duplicate of non-SuppressUnusable question if it is suppressed
// (SuppressQuery is true). The reason for this is that when a question is suppressed, we want an
- // immediate response and not want to be blocked behind a question that is querying DNS servers.
- // When the question is not suppressed, we don't want two active questions sending packets on the wire.
+ // immediate response and not want to be blocked behind a question that is querying DNS servers. When
+ // the question is not suppressed, we don't want two active questions sending packets on the wire.
// This affects both efficiency and also the current design where there is only one active question
// pointed to from a cache entry.
//
{
if (question->Target.type && !ValidQuestionTarget(question))
{
- LogMsg("Warning! Target.type = %ld port = %u (Client forgot to initialize before calling mDNS_StartQuery?)",
- question->Target.type, mDNSVal16(question->TargetPort));
+ LogMsg("mDNS_StartQuery_internal: Warning! Target.type = %ld port = %u (Client forgot to initialize before calling mDNS_StartQuery? for question %##s)",
+ question->Target.type, mDNSVal16(question->TargetPort), question->qname.c);
question->Target.type = mDNSAddrType_None;
}
question->CurrentAnswers = 0;
question->LargeAnswers = 0;
question->UniqueAnswers = 0;
+ question->LOAddressAnswers = 0;
question->FlappingInterface1 = mDNSNULL;
question->FlappingInterface2 = mDNSNULL;
// Must do AuthInfo and SuppressQuery before calling FindDuplicateQuestion()
question->id = zeroOpaque64;
question->validDNSServers = zeroOpaque64;
question->triedAllServersOnce = 0;
- question->noServerResponse = 0;
+ question->noServerResponse = 0;
+ question->StopTime = 0;
if (question->WakeOnResolve)
{
question->WakeOnResolveCount = InitialWakeOnResolveCount;
// Duplicate questions should have the same DNSServers so that when we find
// a matching resource record, all of them get the answers. Calling GetServerForQuestion
// for the duplicate question may get a different DNS server from the original question
+ mDNSu32 timeout = SetValidDNSServers(m, question);
+ // We set the timeout whenever mDNS_StartQuery_internal is called. This means if we have
+ // a networking change/search domain change that calls this function again we keep
+ // reinitializing the timeout value which means it may never timeout. If this becomes
+ // a common case in the future, we can easily fix this by adding extra state that
+ // indicates that we have already set the StopTime.
+ if (question->TimeoutQuestion)
+ question->StopTime = NonZeroTime(m->timenow + timeout * mDNSPlatformOneSecond);
if (question->DuplicateOf)
{
question->validDNSServers = question->DuplicateOf->validDNSServers;
question->qDNSServer = question->DuplicateOf->qDNSServer;
- LogInfo("mDNS_StartQuery_internal: Duplicate question %p (%p) %##s (%s), DNS Server %#a:%d",
- question, question->DuplicateOf, question->qname.c, DNSTypeName(question->qtype),
+ LogInfo("mDNS_StartQuery_internal: Duplicate question %p (%p) %##s (%s), Timeout %d, DNS Server %#a:%d",
+ question, question->DuplicateOf, question->qname.c, DNSTypeName(question->qtype), timeout,
question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL,
mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort));
}
else
{
- SetValidDNSServers(m, question);
question->qDNSServer = GetServerForQuestion(m, question);
- LogInfo("mDNS_StartQuery_internal: question %p %##s (%s), DNS Server %#a:%d",
- question, question->qname.c, DNSTypeName(question->qtype),
+ LogInfo("mDNS_StartQuery_internal: question %p %##s (%s) Timeout %d, DNS Server %#a:%d",
+ question, question->qname.c, DNSTypeName(question->qtype), timeout,
question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL,
mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort));
}
#endif
}
+ else
+ {
+ if (question->TimeoutQuestion)
+ question->StopTime = NonZeroTime(m->timenow + GetTimeoutForMcastQuestion(m, question) * mDNSPlatformOneSecond);
+ }
+ if (question->StopTime) SetNextQueryStopTime(m, question);
SetNextQueryTime(m,question);
}
if (m->NewLocalOnlyQuestions == question) m->NewLocalOnlyQuestions = question->next;
+ if (m->RestartQuestion == question)
+ {
+ LogMsg("mDNS_StopQuery_internal: Just deleted the current restart question: %##s (%s)",
+ question->qname.c, DNSTypeName(question->qtype));
+ m->RestartQuestion = question->next;
+ }
+
// Take care not to trash question->next until *after* we've updated m->CurrentQuestion and m->NewQuestions
question->next = mDNSNULL;
question->ForceMCast = ForceMCast;
question->ReturnIntermed = mDNSfalse;
question->SuppressUnusable = mDNSfalse;
- question->WakeOnResolve = mDNSfalse;
+ question->SearchListIndex = 0;
+ question->AppendSearchDomains = 0;
+ question->RetryWithSearchDomains = mDNSfalse;
+ question->TimeoutQuestion = 0;
+ question->WakeOnResolve = 0;
+ question->qnameOrig = mDNSNULL;
question->QuestionCallback = Callback;
question->QuestionContext = Context;
if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) return(mStatus_BadParamErr);
query->qSRV.ForceMCast = mDNSfalse;
query->qSRV.ReturnIntermed = mDNSfalse;
query->qSRV.SuppressUnusable = mDNSfalse;
- query->qSRV.WakeOnResolve = mDNSfalse;
+ query->qSRV.SearchListIndex = 0;
+ query->qSRV.AppendSearchDomains = 0;
+ query->qSRV.RetryWithSearchDomains = mDNSfalse;
+ query->qSRV.TimeoutQuestion = 0;
+ query->qSRV.WakeOnResolve = 0;
+ query->qSRV.qnameOrig = mDNSNULL;
query->qSRV.QuestionCallback = FoundServiceInfoSRV;
query->qSRV.QuestionContext = query;
query->qTXT.ForceMCast = mDNSfalse;
query->qTXT.ReturnIntermed = mDNSfalse;
query->qTXT.SuppressUnusable = mDNSfalse;
- query->qTXT.WakeOnResolve = mDNSfalse;
+ query->qTXT.SearchListIndex = 0;
+ query->qTXT.AppendSearchDomains = 0;
+ query->qTXT.RetryWithSearchDomains = mDNSfalse;
+ query->qTXT.TimeoutQuestion = 0;
+ query->qTXT.WakeOnResolve = 0;
+ query->qTXT.qnameOrig = mDNSNULL;
query->qTXT.QuestionCallback = FoundServiceInfoTXT;
query->qTXT.QuestionContext = query;
query->qAv4.ForceMCast = mDNSfalse;
query->qAv4.ReturnIntermed = mDNSfalse;
query->qAv4.SuppressUnusable = mDNSfalse;
- query->qAv4.WakeOnResolve = mDNSfalse;
+ query->qAv4.SearchListIndex = 0;
+ query->qAv4.AppendSearchDomains = 0;
+ query->qAv4.RetryWithSearchDomains = mDNSfalse;
+ query->qAv4.TimeoutQuestion = 0;
+ query->qAv4.WakeOnResolve = 0;
+ query->qAv4.qnameOrig = mDNSNULL;
query->qAv4.QuestionCallback = FoundServiceInfo;
query->qAv4.QuestionContext = query;
query->qAv6.ForceMCast = mDNSfalse;
query->qAv6.ReturnIntermed = mDNSfalse;
query->qAv6.SuppressUnusable = mDNSfalse;
- query->qAv6.WakeOnResolve = mDNSfalse;
+ query->qAv6.SearchListIndex = 0;
+ query->qAv6.AppendSearchDomains = 0;
+ query->qAv6.RetryWithSearchDomains = mDNSfalse;
+ query->qAv6.TimeoutQuestion = 0;
+ query->qAv6.WakeOnResolve = 0;
+ query->qAv6.qnameOrig = mDNSNULL;
query->qAv6.QuestionCallback = FoundServiceInfo;
query->qAv6.QuestionContext = query;
question->ForceMCast = mDNSfalse;
question->ReturnIntermed = mDNSfalse;
question->SuppressUnusable = mDNSfalse;
- question->WakeOnResolve = mDNSfalse;
+ question->SearchListIndex = 0;
+ question->AppendSearchDomains = 0;
+ question->RetryWithSearchDomains = mDNSfalse;
+ question->TimeoutQuestion = 0;
+ question->WakeOnResolve = 0;
+ question->qnameOrig = mDNSNULL;
question->QuestionCallback = Callback;
question->QuestionContext = Context;
if (DomainType > mDNS_DomainTypeMax) return(mStatus_BadParamErr);
rr->UpdateCallback = Callback;
#ifndef UNICAST_DISABLED
- if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly && rr->resrec.InterfaceID != mDNSInterface_P2P && !IsLocalDomain(rr->resrec.name))
+ if (rr->ARType != AuthRecordLocalOnly && rr->ARType != AuthRecordP2P && !IsLocalDomain(rr->resrec.name))
{
mStatus status = uDNS_UpdateRecord(m, rr);
// The caller frees the memory on error, don't retain stale pointers
}
#endif
- if (rr->resrec.rroriginalttl == newttl &&
- rr->resrec.rdlength == newrdlength && mDNSPlatformMemSame(rr->resrec.rdata->u.data, newrdata->u.data, newrdlength))
+ if (RRLocalOnly(rr) || (rr->resrec.rroriginalttl == newttl &&
+ rr->resrec.rdlength == newrdlength && mDNSPlatformMemSame(rr->resrec.rdata->u.data, newrdata->u.data, newrdlength)))
CompleteRDataUpdate(m, rr);
else
{
if (!primary) primary = set; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary
// Send dynamic update for non-linklocal IPv4 Addresses
- mDNS_SetupResourceRecord(&set->RR_A, mDNSNULL, set->InterfaceID, kDNSType_A, kHostNameTTL, kDNSRecordTypeUnique, mDNS_HostNameCallback, set);
- mDNS_SetupResourceRecord(&set->RR_PTR, mDNSNULL, set->InterfaceID, kDNSType_PTR, kHostNameTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
- mDNS_SetupResourceRecord(&set->RR_HINFO, mDNSNULL, set->InterfaceID, kDNSType_HINFO, kHostNameTTL, kDNSRecordTypeUnique, mDNSNULL, mDNSNULL);
+ mDNS_SetupResourceRecord(&set->RR_A, mDNSNULL, set->InterfaceID, kDNSType_A, kHostNameTTL, kDNSRecordTypeUnique, AuthRecordAny, mDNS_HostNameCallback, set);
+ mDNS_SetupResourceRecord(&set->RR_PTR, mDNSNULL, set->InterfaceID, kDNSType_PTR, kHostNameTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
+ mDNS_SetupResourceRecord(&set->RR_HINFO, mDNSNULL, set->InterfaceID, kDNSType_HINFO, kHostNameTTL, kDNSRecordTypeUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
#if ANSWER_REMOTE_HOSTNAME_QUERIES
set->RR_A .AllowRemoteQuery = mDNStrue;
NetworkInterfaceInfo **p = &m->HostInterfaces;
if (!set->InterfaceID)
- { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with zero InterfaceID", &set->ip); return(mStatus_Invalid); }
+ { LogMsg("mDNS_RegisterInterface: Error! Tried to register a NetworkInterfaceInfo %#a with zero InterfaceID", &set->ip); return(mStatus_Invalid); }
if (!mDNSAddressIsValidNonZero(&set->mask))
- { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with invalid mask %#a", &set->ip, &set->mask); return(mStatus_Invalid); }
+ { LogMsg("mDNS_RegisterInterface: Error! Tried to register a NetworkInterfaceInfo %#a with invalid mask %#a", &set->ip, &set->mask); return(mStatus_Invalid); }
mDNS_Lock(m);
{
if (*p == set)
{
- LogMsg("Error! Tried to register a NetworkInterfaceInfo that's already in the list");
+ LogMsg("mDNS_RegisterInterface: Error! Tried to register a NetworkInterfaceInfo that's already in the list");
mDNS_Unlock(m);
return(mStatus_AlreadyRegistered);
}
// giving the false impression that there's an active representative of this interface when there really isn't.
// Therefore, when registering an interface, we want to re-trigger our questions and re-probe our Resource Records,
// even if we believe that we previously had an active representative of this interface.
- if (set->McastTxRx && ((m->KnownBugs & mDNS_KnownBug_PhantomInterfaces) || FirstOfType || set->InterfaceActive))
+ if (set->McastTxRx && (FirstOfType || set->InterfaceActive))
{
DNSQuestion *q;
// Normally, after an interface comes up, we pause half a second before beginning probing.
// See <rdar://problem/4073853> mDNS: m->SuppressSending set too enthusiastically
if (!m->SuppressSending) m->SuppressSending = m->timenow + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval);
- if (flapping) LogMsg("RegisterInterface: Frequent transitions for interface %s (%#a)", set->ifname, &set->ip);
+ if (flapping) LogMsg("mDNS_RegisterInterface: Frequent transitions for interface %s (%#a)", set->ifname, &set->ip);
- LogInfo("RegisterInterface: %s (%#a) probedelay %d", set->ifname, &set->ip, probedelay);
+ LogInfo("mDNS_RegisterInterface: %s (%#a) probedelay %d", set->ifname, &set->ip, probedelay);
if (m->SuppressProbes == 0 ||
m->SuppressProbes - NonZeroTime(m->timenow + probedelay) < 0)
m->SuppressProbes = NonZeroTime(m->timenow + probedelay);
+ // Include OWNER option in packets for 60 seconds after connecting to the network. Setting
+ // it here also handles the wake up case as the network link comes UP after waking causing
+ // us to reconnect to the network. If we do this as part of the wake up code, it is possible
+ // that the network link comes UP after 60 seconds and we never set the OWNER option
+ m->AnnounceOwner = NonZeroTime(m->timenow + 60 * mDNSPlatformOneSecond);
+ LogInfo("mDNS_RegisterInterface: Setting AnnounceOwner");
+
for (q = m->Questions; q; q=q->next) // Scan our list of questions
if (mDNSOpaque16IsZero(q->TargetQID))
if (!q->InterfaceID || q->InterfaceID == set->InterfaceID) // If non-specific Q, or Q on this specific interface,
mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping)
{
NetworkInterfaceInfo **p = &m->HostInterfaces;
-
mDNSBool revalidate = mDNSfalse;
- // If this platform has the "phantom interfaces" known bug (e.g. Jaguar), we have to revalidate records every
- // time an interface goes away. Otherwise, when you disconnect the Ethernet cable, the system reports that it
- // still has an IPv6 address, and if we don't revalidate those records don't get deleted in a timely fashion.
- if (m->KnownBugs & mDNS_KnownBug_PhantomInterfaces) revalidate = mDNStrue;
mDNS_Lock(m);
const domainlabel *const name, const domainname *const type, const domainname *const domain,
const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen,
AuthRecord *SubTypes, mDNSu32 NumSubTypes,
- const mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context)
+ mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context, mDNSu32 flags)
{
mStatus err;
mDNSu32 i;
+ mDNSu32 hostTTL;
+ AuthRecType artype;
+ mDNSu8 recordType = (flags & regFlagKnownUnique) ? kDNSRecordTypeKnownUnique : kDNSRecordTypeUnique;
sr->ServiceCallback = Callback;
sr->ServiceContext = Context;
sr->NumSubTypes = NumSubTypes;
sr->SubTypes = SubTypes;
+ if (InterfaceID == mDNSInterface_LocalOnly)
+ artype = AuthRecordLocalOnly;
+ else if (InterfaceID == mDNSInterface_P2P)
+ artype = AuthRecordP2P;
+ else if ((InterfaceID == mDNSInterface_Any) && (flags & regFlagIncludeP2P))
+ artype = AuthRecordAnyIncludeP2P;
+ else
+ artype = AuthRecordAny;
+
// Initialize the AuthRecord objects to sane values
// Need to initialize everything correctly *before* making the decision whether to do a RegisterNoSuchService and bail out
- mDNS_SetupResourceRecord(&sr->RR_ADV, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeAdvisory, ServiceCallback, sr);
- mDNS_SetupResourceRecord(&sr->RR_PTR, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, ServiceCallback, sr);
- mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique, ServiceCallback, sr);
- mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, InterfaceID, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnique, ServiceCallback, sr);
+ mDNS_SetupResourceRecord(&sr->RR_ADV, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeAdvisory, artype, ServiceCallback, sr);
+ mDNS_SetupResourceRecord(&sr->RR_PTR, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, artype, ServiceCallback, sr);
+
+ if (SameDomainName(type, (const domainname *) "\x4" "_ubd" "\x4" "_tcp"))
+ hostTTL = kHostNameSmallTTL;
+ else
+ hostTTL = kHostNameTTL;
+
+ mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, InterfaceID, kDNSType_SRV, hostTTL, recordType, artype, ServiceCallback, sr);
+ mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, InterfaceID, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnique, artype, ServiceCallback, sr);
// If port number is zero, that means the client is really trying to do a RegisterNoSuchService
if (mDNSIPPortIsZero(port))
- return(mDNS_RegisterNoSuchService(m, &sr->RR_SRV, name, type, domain, mDNSNULL, mDNSInterface_Any, NSSCallback, sr));
+ return(mDNS_RegisterNoSuchService(m, &sr->RR_SRV, name, type, domain, mDNSNULL, InterfaceID, NSSCallback, sr, (flags & regFlagIncludeP2P)));
// If the client is registering an oversized TXT record,
// it is the client's responsibility to alloate a ServiceRecordSet structure that is large enough for it
AssignDomainName(&st, sr->SubTypes[i].resrec.name);
st.c[1+st.c[0]] = 0; // Only want the first label, not the whole FQDN (particularly for mDNS_RenameAndReregisterService())
AppendDomainName(&st, type);
- mDNS_SetupResourceRecord(&sr->SubTypes[i], mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, ServiceCallback, sr);
+ mDNS_SetupResourceRecord(&sr->SubTypes[i], mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, artype, ServiceCallback, sr);
if (ConstructServiceName(&sr->SubTypes[i].namestorage, mDNSNULL, &st, domain) == mDNSNULL) return(mStatus_BadParamErr);
AssignDomainName(&sr->SubTypes[i].resrec.rdata->u.name, &sr->RR_SRV.namestorage);
sr->SubTypes[i].Additional1 = &sr->RR_SRV;
// that if the SRV cannot find a target, rest of the records that belong to this service
// will not be activated.
err = mDNS_Register_internal(m, &sr->RR_SRV);
+ // If we can't register the SRV record due to errors, bail out. It has not been inserted in
+ // any list and hence no need to deregister. We could probably do similar checks for other
+ // records below and bail out. For now, this seems to be sufficient to address rdar://9304275
+ if (err)
+ {
+ mDNS_Unlock(m);
+ return err;
+ }
if (!err) err = mDNS_Register_internal(m, &sr->RR_TXT);
// We register the RR_PTR last, because we want to be sure that in the event of a forced call to
// mDNS_StartExit, the RR_PTR will be the last one to be forcibly deregistered, since that is what triggers
}
mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr,
- ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl)
+ ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl, mDNSu32 includeP2P)
{
ExtraResourceRecord **e;
mStatus status;
+ AuthRecType artype;
+ mDNSInterfaceID InterfaceID = sr->RR_PTR.resrec.InterfaceID;
+
+ if (InterfaceID == mDNSInterface_LocalOnly)
+ artype = AuthRecordLocalOnly;
+ if (InterfaceID == mDNSInterface_P2P)
+ artype = AuthRecordP2P;
+ else if ((InterfaceID == mDNSInterface_Any) && includeP2P)
+ artype = AuthRecordAnyIncludeP2P;
+ else
+ artype = AuthRecordAny;
extra->next = mDNSNULL;
mDNS_SetupResourceRecord(&extra->r, rdata, sr->RR_PTR.resrec.InterfaceID,
- extra->r.resrec.rrtype, ttl, kDNSRecordTypeUnique, ServiceCallback, sr);
+ extra->r.resrec.rrtype, ttl, kDNSRecordTypeUnique, artype, ServiceCallback, sr);
AssignDomainName(&extra->r.namestorage, sr->RR_SRV.resrec.name);
mDNS_Lock(m);
err = mDNS_RegisterService(m, sr, newname, &type, &domain,
host, sr->RR_SRV.resrec.rdata->u.srv.port, sr->RR_TXT.resrec.rdata->u.txt.c, sr->RR_TXT.resrec.rdlength,
sr->SubTypes, sr->NumSubTypes,
- sr->RR_PTR.resrec.InterfaceID, sr->ServiceCallback, sr->ServiceContext);
+ sr->RR_PTR.resrec.InterfaceID, sr->ServiceCallback, sr->ServiceContext, 0);
// mDNS_RegisterService() just reset sr->Extras to NULL.
// Fortunately we already grabbed ourselves a copy of this pointer (above), so we can now run
{
ExtraResourceRecord *e = extras;
extras = extras->next;
- err = mDNS_AddRecordToService(m, sr, e, e->r.resrec.rdata, e->r.resrec.rroriginalttl);
+ err = mDNS_AddRecordToService(m, sr, e, e->r.resrec.rdata, e->r.resrec.rroriginalttl, 0);
}
return(err);
mDNSexport mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const rr,
const domainlabel *const name, const domainname *const type, const domainname *const domain,
const domainname *const host,
- const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context)
+ const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context, mDNSBool includeP2P)
{
- mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique, Callback, Context);
+ AuthRecType artype;
+
+ if (InterfaceID == mDNSInterface_LocalOnly)
+ artype = AuthRecordLocalOnly;
+ else if (InterfaceID == mDNSInterface_P2P)
+ artype = AuthRecordP2P;
+ else if ((InterfaceID == mDNSInterface_Any) && includeP2P)
+ artype = AuthRecordAnyIncludeP2P;
+ else
+ artype = AuthRecordAny;
+
+ mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique, artype, Callback, Context);
if (ConstructServiceName(&rr->namestorage, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr);
rr->resrec.rdata->u.srv.priority = 0;
rr->resrec.rdata->u.srv.weight = 0;
mDNSexport mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr,
mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname)
{
- mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, mDNSNULL, mDNSNULL);
+ AuthRecType artype;
+
+ if (InterfaceID == mDNSInterface_LocalOnly)
+ artype = AuthRecordLocalOnly;
+ else if (InterfaceID == mDNSInterface_P2P)
+ artype = AuthRecordP2P;
+ else
+ artype = AuthRecordAny;
+ mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, artype, mDNSNULL, mDNSNULL);
if (!MakeDomainNameFromDNSNameString(&rr->namestorage, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr);
if (!MakeDomainNameFromDNSNameString(&rr->resrec.rdata->u.name, domname)) return(mStatus_BadParamErr);
return(mDNS_Register(m, rr));
(mDNSu8 *)"", 1, // TXT data, length
mDNSNULL, 0, // Subtypes (none)
mDNSInterface_Any, // Interface ID
- SleepProxyServerCallback, mDNSNULL); // Callback and context
+ SleepProxyServerCallback, mDNSNULL, 0); // Callback, context, flags
}
LogSPS("Sleep Proxy Server %#s %s", srs->RR_SRV.resrec.name->c, m->SPSState ? "started" : "stopped");
}
m->NextScheduledResponse = timenow + 0x78000000;
m->NextScheduledNATOp = timenow + 0x78000000;
m->NextScheduledSPS = timenow + 0x78000000;
+ m->NextScheduledStopTime = timenow + 0x78000000;
m->RandomQueryDelay = 0;
m->RandomReconfirmDelay = 0;
m->PktNum = 0;
m->SleepState = SleepState_Awake;
m->SleepSeqNum = 0;
m->SystemWakeOnLANEnabled = mDNSfalse;
- m->SentSleepProxyRegistration = mDNSfalse;
m->AnnounceOwner = NonZeroTime(timenow + 60 * mDNSPlatformOneSecond);
m->DelaySleep = 0;
m->SleepLimit = 0;
m->CurrentQuestion = mDNSNULL;
m->LocalOnlyQuestions = mDNSNULL;
m->NewLocalOnlyQuestions = mDNSNULL;
+ m->RestartQuestion = mDNSNULL;
m->rrcache_size = 0;
m->rrcache_totalused = 0;
m->rrcache_active = 0;
}
mDNS_GrowCache_internal(m, rrcachestorage, rrcachesize);
+ m->rrauth.rrauth_free = mDNSNULL;
+
+ for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+ m->rrauth.rrauth_hash[slot] = mDNSNULL;
// Fields below only required for mDNS Responder...
m->hostlabel.c[0] = 0;
m->ResourceRecords = mDNSNULL;
m->DuplicateRecords = mDNSNULL;
m->NewLocalRecords = mDNSNULL;
+ m->NewLocalOnlyRecords = mDNSfalse;
m->CurrentRecord = mDNSNULL;
m->HostInterfaces = mDNSNULL;
m->ProbeFailTime = 0;
DNSServer *ptr, **p = &m->DNSServers;
const DNSServer *oldServers = m->DNSServers;
DNSQuestion *q;
+ McastResolver *mr, **mres = &m->McastResolvers;
debugf("uDNS_SetupDNSConfig: entry");
// Let the platform layer get the current DNS information
- // The m->StartWABQueries boolean is so that we lazily get the search domain list only on-demand
- // and start the domain enumeration queries. (no need to hit the network with domain enumeration
- // queries until we actually need that information).
+ // The m->StartWABQueries is set when we get the first domain enumeration query (no need to hit the network
+ // with domain enumeration queries until we actually need that information). Even if it is not set, we still
+ // need to setup the search domains so that we can append them to queries that need them.
- uDNS_SetupSearchDomains(m, m->StartWABQueries ? (UDNS_START_WAB_QUERY | UDNS_START_CF_QUERY) :
- (UDNS_START_CF_QUERY));
+ uDNS_SetupSearchDomains(m, m->StartWABQueries ? UDNS_START_WAB_QUERY : 0);
mDNS_Lock(m);
ptr->flags |= DNSServer_FlagDelete;
}
+ // We handle the mcast resolvers here itself as mDNSPlatformSetDNSConfig looks at
+ // mcast resolvers. Today we get both mcast and ucast configuration using the same
+ // API
+ for (mr = m->McastResolvers; mr; mr = mr->next)
+ mr->flags |= McastResolver_FlagDelete;
+
mDNSPlatformSetDNSConfig(m, mDNStrue, mDNSfalse, &fqdn, mDNSNULL, mDNSNULL);
+ // For now, we just delete the mcast resolvers. We don't deal with cache or
+ // questions here. Neither question nor cache point to mcast resolvers. Questions
+ // do inherit the timeout values from mcast resolvers. But we don't bother
+ // affecting them as they never change.
+ while (*mres)
+ {
+ if (((*mres)->flags & DNSServer_FlagDelete) != 0)
+ {
+ mr = *mres;
+ *mres = (*mres)->next;
+ debugf("uDNS_SetupDNSConfig: Deleting mcast resolver %##s", mr, mr->domain.c);
+ mDNSPlatformMemFree(mr);
+ }
+ else
+ {
+ (*mres)->flags &= ~McastResolver_FlagNew;
+ mres = &(*mres)->next;
+ }
+ }
+
// Mark the records to be flushed that match a new resolver. We need to do this before
// we walk the questions below where we change the DNSServer pointer of the cache
// record
mDNS_PurgeCacheResourceRecord(m, cr);
}
else
+ {
+ LogInfo("uDNS_SetupDNSConfig: Purging/Reconfirming Resourcerecord %s", CRDisplayString(m, cr));
PurgeOrReconfirmCacheRecord(m, cr, ptr, mDNSfalse);
+ }
}
}
// Update our qDNSServer pointers before we go and free the DNSServer object memory
mStatus_NATPortMappingDisabled = -65565, // NAT supports NAT-PMP or UPnP but it's disabled by the administrator
mStatus_NoRouter = -65566,
mStatus_PollingMode = -65567,
+ mStatus_Timeout = -65568,
// -65568 to -65786 currently unused; available for allocation
// tcp connection status
#define kStandardTTL (3600UL * 100 / 80)
#define kHostNameTTL 120UL
+// Some applications want to register their SRV records with a lower ttl so that in case the server
+// using a dynamic port number restarts, the clients will not have stale information for more than
+// 10 seconds
+
+#define kHostNameSmallTTL 10UL
+
+
// Multicast DNS uses announcements (gratuitous responses) to update peer caches.
// This means it is feasible to use relatively larger TTL values than we might otherwise
// use, because we have a cache coherency protocol to keep the peer caches up to date.
typedef struct ServiceRecordSet_struct ServiceRecordSet;
typedef struct CacheRecord_struct CacheRecord;
typedef struct CacheGroup_struct CacheGroup;
+typedef struct AuthGroup_struct AuthGroup;
typedef struct DNSQuestion_struct DNSQuestion;
typedef struct ZoneData_struct ZoneData;
typedef struct mDNS_struct mDNS;
DNSServer_FlagNew = 2
};
+enum
+ {
+ McastResolver_FlagDelete = 1,
+ McastResolver_FlagNew = 2
+ };
+
+typedef struct McastResolver
+ {
+ struct McastResolver *next;
+ mDNSInterfaceID interface;
+ mDNSu32 flags; // Set when we're planning to delete this from the list
+ domainname domain;
+ mDNSu32 timeout; // timeout value for questions
+ } McastResolver;
+
typedef struct DNSServer
{
struct DNSServer *next;
mDNSs32 penaltyTime; // amount of time this server is penalized
mDNSBool scoped; // interface should be matched against question only
// if scoped is set
+ mDNSu32 timeout; // timeout value for questions
} DNSServer;
typedef struct // Size is 36 bytes when compiling for 32-bit; 48 when compiling for 64-bit
mergeState_DontMerge = 1 // Set on fatal error conditions to disable merging
} mergeState_t;
+struct AuthGroup_struct // Header object for a list of AuthRecords with the same name
+ {
+ AuthGroup *next; // Next AuthGroup object in this hash table bucket
+ mDNSu32 namehash; // Name-based (i.e. case insensitive) hash of name
+ AuthRecord *members; // List of CacheRecords with this same name
+ AuthRecord **rrauth_tail; // Tail end of that list
+ domainname *name; // Common name for all AuthRecords in this list
+ AuthRecord *NewLocalOnlyRecords;
+ // Size to here is 20 bytes when compiling 32-bit; 40 bytes when compiling 64-bit
+ mDNSu8 namestorage[InlineCacheGroupNameSize];
+ };
+
+#define AUTH_HASH_SLOTS 499
+#define FORALL_AUTHRECORDS(SLOT,AG,AR) \
+ for ((SLOT) = 0; (SLOT) < AUTH_HASH_SLOTS; (SLOT)++) \
+ for ((AG)=m->rrauth.rrauth_hash[(SLOT)]; (AG); (AG)=(AG)->next) \
+ for ((AR) = (AG)->members; (AR); (AR)=(AR)->next)
+
+typedef union AuthEntity_union AuthEntity;
+union AuthEntity_union { AuthEntity *next; AuthGroup ag; };
+typedef struct {
+ mDNSu32 rrauth_size; // Total number of available auth entries
+ mDNSu32 rrauth_totalused; // Number of auth entries currently occupied
+ mDNSu32 rrauth_report;
+ mDNSu8 rrauth_lock; // For debugging: Set at times when these lists may not be modified
+ AuthEntity *rrauth_free;
+ AuthGroup *rrauth_hash[AUTH_HASH_SLOTS];
+}AuthHash;
+
+// AuthRecordAny includes mDNSInterface_Any and interface specific auth records (anything
+// other than P2P or LocalOnly)
+typedef enum
+ {
+ AuthRecordAny, // registered for *Any, NOT including P2P interfaces
+ AuthRecordAnyIncludeP2P, // registered for *Any, including P2P interfaces
+ AuthRecordLocalOnly,
+ AuthRecordP2P // discovered over D2D/P2P framework
+ } AuthRecType;
+
struct AuthRecord_struct
{
// For examples of how to set up this structure for use in mDNS_Register(),
mDNSAddr AddressProxy; // For reverse-mapping Sleep Proxy PTR records, address in question
mDNSs32 TimeRcvd; // In platform time units
mDNSs32 TimeExpire; // In platform time units
+ AuthRecType ARType; // LocalOnly, P2P or Normal ?
// Field Group 3: Transient state for Authoritative Records
mDNSu8 Acknowledged; // Set if we've given the success callback to the client
#define Question_uDNS(Q) ((Q)->InterfaceID == mDNSInterface_Unicast || \
((Q)->InterfaceID != mDNSInterface_LocalOnly && (Q)->InterfaceID != mDNSInterface_P2P && !(Q)->ForceMCast && !IsLocalDomain(&(Q)->qname)))
+#define RRLocalOnly(rr) ((rr)->ARType == AuthRecordLocalOnly || (rr)->ARType == AuthRecordP2P)
+
+#define RRAny(rr) ((rr)->ARType == AuthRecordAny || (rr)->ARType == AuthRecordAnyIncludeP2P)
+
// Question (A or AAAA) that is suppressed currently because IPv4 or IPv6 address
// is not available locally for A or AAAA question respectively
#define QuerySuppressed(Q) ((Q)->SuppressUnusable && (Q)->SuppressQuery)
#define PrivateQuery(Q) ((Q)->AuthInfo && (Q)->AuthInfo->AutoTunnel)
+// Normally we always lookup the cache and /etc/hosts before sending the query on the wire. For single label
+// queries (A and AAAA) that are unqualified (indicated by AppendSearchDomains), we want to append search
+// domains before we try them as such
+#define ApplySearchDomainsFirst(q) ((q)->AppendSearchDomains && (CountLabels(&((q)->qname))) == 1)
+
// Wrapper struct for Auth Records for higher-level code that cannot use the AuthRecord's ->next pointer field
typedef struct ARListElem
{
mDNSu8 namestorage[InlineCacheGroupNameSize];
};
+
struct CacheRecord_struct
{
CacheRecord *next; // Next in list; first element of structure for efficiency reasons
{
struct DomainAuthInfo *next;
mDNSs32 deltime; // If we're planning to delete this DomainAuthInfo, the time we want it deleted
- mDNSBool AutoTunnel;
+ const char* AutoTunnel; // If NULL, this is not an AutoTunnel DAI. Otherwise, this is prepended to the IPSec identifier
AuthRecord AutoTunnelHostRecord; // User-visible hostname; used as SRV target for AutoTunnel services
AuthRecord AutoTunnelTarget; // Opaque hostname of tunnel endpoint; used as SRV target for AutoTunnelService record
AuthRecord AutoTunnelDeviceInfo; // Device info of tunnel endpoint
NATTraversalInfo AutoTunnelNAT;
domainname domain;
domainname keyname;
+ domainname hostname;
+ mDNSIPPort port;
char b64keydata[32];
mDNSu8 keydata_ipad[HMAC_LEN]; // padded key for inner hash rounds
mDNSu8 keydata_opad[HMAC_LEN]; // padded key for outer hash rounds
mDNSs32 LastQTxTime; // Last time this Q was sent on one (but not necessarily all) interfaces
mDNSu32 CNAMEReferrals; // Count of how many CNAME redirections we've done
mDNSBool SuppressQuery; // This query should be suppressed and not sent on the wire
+ mDNSu8 LOAddressAnswers; // Number of answers from the local only auth records that are
+ // answering A, AAAA and CNAME (/etc/hosts)
mDNSu8 WakeOnResolveCount; // Number of wakes that should be sent on resolve
+ mDNSs32 StopTime; // Time this question should be stopped by giving them a negative answer
// Wide Area fields. These are used internally by the uDNS core
UDPSocket *LocalSocket;
mDNSOpaque64 validDNSServers; // Valid DNSServers for this question
mDNSu16 noServerResponse; // At least one server did not respond.
mDNSu16 triedAllServersOnce; // Tried all DNS servers once
- mDNSu8 unansweredQueries;// The number of unanswered queries to this server
+ mDNSu8 unansweredQueries;// The number of unanswered queries to this server
ZoneData *nta; // Used for getting zone data for private or LLQ query
mDNSAddr servAddr; // Address and port learned from _dns-llq, _dns-llq-tls or _dns-query-tls SRV query
mDNSBool ForceMCast; // Set by client to force mDNS query, even for apparently uDNS names
mDNSBool ReturnIntermed; // Set by client to request callbacks for intermediate CNAME/NXDOMAIN results
mDNSBool SuppressUnusable; // Set by client to suppress unusable queries to be sent on the wire
- mDNSBool WakeOnResolve; // Send wakeup on resolve
+ mDNSBool RetryWithSearchDomains; // Retry with search domains if there is no entry in the cache or AuthRecords
+ mDNSu8 TimeoutQuestion; // Timeout this question if there is no reply in configured time
+ mDNSu8 WakeOnResolve; // Send wakeup on resolve
+ mDNSs8 SearchListIndex; // Index into SearchList; Used by the client layer but not touched by core
+ mDNSs8 AppendSearchDomains; // Search domains can be appended for this query
+ mDNSs8 AppendLocalSearchDomains; // Search domains ending in .local can be appended for this query
+ domainname *qnameOrig; // Copy of the original question name if it is not fully qualified
mDNSQuestionCallback *QuestionCallback;
void *QuestionContext;
};
extern ZoneData *StartGetZoneData(mDNS *const m, const domainname *const name, const ZoneService target, ZoneDataCallback callback, void *callbackInfo);
extern void CancelGetZoneData(mDNS *const m, ZoneData *nta);
+extern mDNSBool IsGetZoneDataQuestion(DNSQuestion *q);
typedef struct DNameListElem
{
typedef struct ClientTunnel
{
struct ClientTunnel *next;
+ const char *prefix;
domainname dstname;
mDNSBool MarkedForDeletion;
mDNSv6Addr loc_inner;
mDNSu8 Advertise; // False if you are only searching on this interface
mDNSu8 McastTxRx; // Send/Receive multicast on this { InterfaceID, address family } ?
mDNSu8 NetWake; // Set if Wake-On-Magic-Packet is enabled on this interface
+ mDNSu8 Loopback; // Set if this is the loopback interface
};
#define SLE_DELETE 0x00000001
#define SLE_WAB_QUERY_STARTED 0x00000002
-#define SLE_CF_QUERY_STARTED 0x00000004
typedef struct SearchListElem
{
struct SearchListElem *next;
domainname domain;
- int flag; // -1 means delete, 0 means unchanged, +1 means newly added
+ int flag;
+ mDNSInterfaceID InterfaceID;
DNSQuestion BrowseQ;
DNSQuestion DefBrowseQ;
DNSQuestion AutomaticBrowseQ;
DNSQuestion RegisterQ;
DNSQuestion DefRegisterQ;
- DNSQuestion CfQ;
int numCfAnswers;
ARListElem *AuthRecs;
} SearchListElem;
enum // Bit flags -- i.e. values should be 1, 2, 4, 8, etc.
{
- mDNS_KnownBug_PhantomInterfaces = 1,
- mDNS_KnownBug_LimitedIPv6 = 2,
- mDNS_KnownBug_LossySyslog = 4 // <rdar://problem/6561888>
+ mDNS_KnownBug_LimitedIPv6 = 1,
+ mDNS_KnownBug_LossySyslog = 2 // <rdar://problem/6561888>
};
enum
mDNSs32 NextScheduledSPRetry; // Time next sleep proxy registration action is required.
// Only valid if SleepLimit is nonzero and DelaySleep is zero.
+ mDNSs32 NextScheduledStopTime; // Next time to stop a question
+
// These fields only required for mDNS Searcher...
DNSQuestion *Questions; // List of all registered questions, active and inactive
DNSQuestion *NewQuestions; // Fresh questions not yet answered from cache
DNSQuestion *CurrentQuestion; // Next question about to be examined in AnswerLocalQuestions()
DNSQuestion *LocalOnlyQuestions; // Questions with InterfaceID set to mDNSInterface_LocalOnly or mDNSInterface_P2P
DNSQuestion *NewLocalOnlyQuestions; // Fresh local-only or P2P questions not yet answered
+ DNSQuestion *RestartQuestion; // Questions that are being restarted (stop followed by start)
mDNSu32 rrcache_size; // Total number of available cache entries
mDNSu32 rrcache_totalused; // Number of cache entries currently occupied
mDNSu32 rrcache_active; // Number of cache entries currently occupied by records that answer active questions
CacheGroup *rrcache_hash[CACHE_HASH_SLOTS];
mDNSs32 rrcache_nextcheck[CACHE_HASH_SLOTS];
+ AuthHash rrauth;
+
// Fields below only required for mDNS Responder...
domainlabel nicelabel; // Rich text label encoded using canonically precomposed UTF-8
domainlabel hostlabel; // Conforms to RFC 1034 "letter-digit-hyphen" ARPANET host name rules
AuthRecord DeviceInfo;
AuthRecord *ResourceRecords;
AuthRecord *DuplicateRecords; // Records currently 'on hold' because they are duplicates of existing records
- AuthRecord *NewLocalRecords; // Fresh AuthRecords (both local-only and public) not yet delivered to our local-only questions
+ AuthRecord *NewLocalRecords; // Fresh AuthRecords (public) not yet delivered to our local-only questions
AuthRecord *CurrentRecord; // Next AuthRecord about to be examined
+ mDNSBool NewLocalOnlyRecords; // Fresh AuthRecords (local only) not yet delivered to our local questions
NetworkInterfaceInfo *HostInterfaces;
mDNSs32 ProbeFailTime;
mDNSu32 NumFailedProbes;
mDNSs32 NextSRVUpdate; // Time to perform delayed update
DNSServer *DNSServers; // list of DNS servers
+ McastResolver *McastResolvers; // list of Mcast Resolvers
mDNSAddr Router;
mDNSAddr AdvertisedV4; // IPv4 address pointed to by hostname
mDNSv6Addr AutoTunnelRelayAddrOut;
domainlabel AutoTunnelLabel; // Used to construct hostname for *IPv4* address of tunnel endpoints
- mDNSBool StartWABQueries;
+ mDNSBool StartWABQueries; // Start WAB queries for the purpose of domain enumeration
mDNSBool RegisterAutoTunnel6;
// NAT-Traversal fields
extern void mDNS_ConfigChanged(mDNS *const m);
extern void mDNS_GrowCache (mDNS *const m, CacheEntity *storage, mDNSu32 numrecords);
+extern void mDNS_GrowAuth (mDNS *const m, AuthEntity *storage, mDNSu32 numrecords);
extern void mDNS_StartExit (mDNS *const m);
extern void mDNS_FinalExit (mDNS *const m);
#define mDNS_Close(m) do { mDNS_StartExit(m); mDNS_FinalExit(m); } while(0)
// and the default domain in which to register in the case where the user has made no selection.
extern void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID,
- mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, mDNSRecordCallback Callback, void *Context);
+ mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, AuthRecType artype, mDNSRecordCallback Callback, void *Context);
+
+// mDNS_RegisterService() flags parameter bit definitions
+enum
+ {
+ regFlagIncludeP2P = 0x1, // include P2P interfaces when using mDNSInterface_Any
+ regFlagKnownUnique = 0x2 // client guarantees that SRV and TXT record names are unique
+ };
extern mStatus mDNS_RegisterService (mDNS *const m, ServiceRecordSet *sr,
const domainlabel *const name, const domainname *const type, const domainname *const domain,
const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen,
AuthRecord *SubTypes, mDNSu32 NumSubTypes,
- const mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context);
-extern mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl);
+ mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context, mDNSu32 flags);
+extern mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl, mDNSu32 includeP2P);
extern mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, mDNSRecordCallback MemFreeCallback, void *Context);
extern mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordSet *const sr, const domainlabel *newname);
extern mStatus mDNS_DeregisterService_drt(mDNS *const m, ServiceRecordSet *sr, mDNS_Dereg_type drt);
extern mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const rr,
const domainlabel *const name, const domainname *const type, const domainname *const domain,
const domainname *const host,
- const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context);
+ const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context, mDNSBool includeP2P);
#define mDNS_DeregisterNoSuchService mDNS_Deregister
extern void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID InterfaceID, const domainname *const name,
extern DNSServer *GetServerForName(mDNS *m, const domainname *name, mDNSInterfaceID InterfaceID);
extern DNSServer *GetServerForQuestion(mDNS *m, DNSQuestion *question);
-extern void SetValidDNSServers(mDNS *m, DNSQuestion *question);
+extern mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question);
// ***************************************************************************
#if 0
#define mDNSv4AddressIsLoopback(X) ((X)->b[0] == 127 && (X)->b[1] == 0 && (X)->b[2] == 0 && (X)->b[3] == 1)
#define mDNSv6AddressIsLoopback(X) ((((X)->l[0] | (X)->l[1] | (X)->l[2]) == 0) && ((X)->b[12] == 0 && (X)->b[13] == 0 && (X)->b[14] == 0 && (X)->b[15] == 1))
-#define mDNSAddressIsLoopback(X) ( \
- ((X)->type == mDNSAddrType_IPv4) ? mDNSv4AddressIsLoopback(&(X)->ip.v4) : \
- ((X)->type == mDNSAddrType_IPv6) ? mDNSv6AddressIsLoopback(&(X)->ip.v6) : mDNSfalse)
// ***************************************************************************
#if 0
#pragma mark -
// domain name format. The shared secret must be a null-terminated base64 encoded string. A minimum size of
// 16 bytes (128 bits) is recommended for an MD5 hash as per RFC 2485.
// Calling this routine multiple times for a zone replaces previously entered values. Call with a NULL key
-// to dissable authentication for the zone.
+// to disable authentication for the zone. A non-NULL autoTunnelPrefix means this is an AutoTunnel domain,
+// and the value is prepended to the IPSec identifier (used for key lookup)
extern mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info,
- const domainname *domain, const domainname *keyname, const char *b64keydata, mDNSBool AutoTunnel);
+ const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, const char *autoTunnelPrefix);
extern void RecreateNATMappings(mDNS *const m);
extern void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext);
extern void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn);
extern void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, const mDNSAddr *v6addr, const mDNSAddr *router);
-extern DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped);
+extern DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout);
extern void PenalizeDNSServer(mDNS *const m, DNSQuestion *q);
-extern void mDNS_AddSearchDomain(const domainname *const domain);
+extern void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID);
+
+extern McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, mDNSu32 timeout);
// We use ((void *)0) here instead of mDNSNULL to avoid compile warnings on gcc 4.2
-#define mDNS_AddSearchDomain_CString(X) \
- do { domainname d__; if (((X) != (void*)0) && MakeDomainNameFromDNSNameString(&d__, (X)) && d__.c[0]) mDNS_AddSearchDomain(&d__); } while(0)
+#define mDNS_AddSearchDomain_CString(X, I) \
+ do { domainname d__; if (((X) != (void*)0) && MakeDomainNameFromDNSNameString(&d__, (X)) && d__.c[0]) mDNS_AddSearchDomain(&d__, I); } while(0)
// Routines called by the core, exported by DNSDigest.c
// and interface indexes in order to support the DNS-SD API. If your target platform does not support
// multiple interfaces and/or does not support the DNS-SD API, these functions can be empty.
extern mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(mDNS *const m, mDNSu32 ifindex);
-extern mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id);
+extern mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id, mDNSBool suppressNetworkChange);
// Every platform support module must provide the following functions if it is to support unicast DNS
// and Dynamic Update.
extern mStatus mDNSPlatformGetPrimaryInterface(mDNS *const m, mDNSAddr *v4, mDNSAddr *v6, mDNSAddr *router);
extern void mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const dname, const mStatus status);
-extern void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep);
+extern void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason);
extern void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID InterfaceID, char *EthAddr, char *IPAddr, int iteration);
+extern mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf);
#ifdef _LEGACY_NAT_TRAVERSAL_
// Support for legacy NAT traversal protocols, implemented by the platform layer and callable by the core.
extern void mDNSCoreReceive(mDNS *const m, void *const msg, const mDNSu8 *const end,
const mDNSAddr *const srcaddr, const mDNSIPPort srcport,
const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID);
-extern void mDNSCoreRestartQueries(mDNS *const m);
+extern void mDNSCoreRestartQueries(mDNS *const m);
+typedef void (*FlushCache)(mDNS *const m);
+typedef void (*CallbackBeforeStartQuery)(mDNS *const m, void *context);
+extern void mDNSCoreRestartAddressQueries(mDNS *const m, mDNSBool SearchDomainsChanged, FlushCache flushCacheRecords,
+ CallbackBeforeStartQuery beforeQueryStart, void *context);
extern mDNSBool mDNSCoreHaveAdvertisedMulticastServices(mDNS *const m);
extern void mDNSCoreMachineSleep(mDNS *const m, mDNSBool wake);
extern mDNSBool mDNSCoreReadyForSleep(mDNS *m, mDNSs32 now);
extern void CompleteDeregistration(mDNS *const m, AuthRecord *rr);
extern void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheRecord *const rr, const QC_result AddRecord);
extern char *InterfaceNameForID(mDNS *const m, const mDNSInterfaceID InterfaceID);
-extern void DNSServerChangeForQuestion(mDNS *const m, DNSQuestion *q, DNSServer *new);
+extern void DNSServerChangeForQuestion(mDNS *const m, DNSQuestion *q, DNSServer *newServer);
extern void ActivateUnicastRegistration(mDNS *const m, AuthRecord *const rr);
extern void CheckSuppressUnusableQuestions(mDNS *const m);
+extern void RetrySearchDomainQuestions(mDNS *const m);
+
+// Used only in logging to restrict the number of /etc/hosts entries printed
+extern void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result);
+// exported for using the hash for /etc/hosts AuthRecords
+extern AuthGroup *AuthGroupForName(AuthHash *r, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name);
+extern AuthGroup *AuthGroupForRecord(AuthHash *r, const mDNSu32 slot, const ResourceRecord *const rr);
+extern AuthGroup *InsertAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr);
+extern AuthGroup *RemoveAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr);
// 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
extern void UpdateAutoTunnelDomainStatuses(const mDNS *const m);
extern mStatus ActivateLocalProxy(mDNS *const m, char *ifname);
extern void RemoveAutoTunnel6Record(mDNS *const m);
-extern void SetupConndConfigChanges(mDNS *const m);
extern mDNSBool RecordReadyForSleep(mDNS *const m, AuthRecord *rr);
#endif
// 60 = 1 W
// 90 = 1 kW
+typedef enum
+ {
+ mDNSSleepProxyMetric_Dedicated = 20,
+ mDNSSleepProxyMetric_PrimaryHardware = 30,
+ mDNSSleepProxyMetric_PrimarySoftware = 40,
+ mDNSSleepProxyMetric_SecondaryHardware = 50,
+ mDNSSleepProxyMetric_SecondarySoftware = 60,
+ mDNSSleepProxyMetric_IncidentalHardware = 70,
+ mDNSSleepProxyMetric_IncidentalSoftware = 80
+ } mDNSSleepProxyMetric;
+
extern void mDNSCoreBeSleepProxyServer_internal(mDNS *const m, mDNSu8 sps, mDNSu8 port, mDNSu8 marginalpower, mDNSu8 totpower);
#define mDNSCoreBeSleepProxyServer(M,S,P,MP,TP) \
do { mDNS_Lock(m); mDNSCoreBeSleepProxyServer_internal((M),(S),(P),(MP),(TP)); mDNS_Unlock(m); } while(0)
char sizecheck_AuthRecord [(sizeof(AuthRecord) <= 1208) ? 1 : -1];
char sizecheck_CacheRecord [(sizeof(CacheRecord) <= 184) ? 1 : -1];
char sizecheck_CacheGroup [(sizeof(CacheGroup) <= 184) ? 1 : -1];
- char sizecheck_DNSQuestion [(sizeof(DNSQuestion) <= 762) ? 1 : -1];
- char sizecheck_ZoneData [(sizeof(ZoneData) <= 1598) ? 1 : -1];
+ char sizecheck_DNSQuestion [(sizeof(DNSQuestion) <= 786) ? 1 : -1];
+ char sizecheck_ZoneData [(sizeof(ZoneData) <= 1624) ? 1 : -1];
char sizecheck_NATTraversalInfo [(sizeof(NATTraversalInfo) <= 192) ? 1 : -1];
char sizecheck_HostnameInfo [(sizeof(HostnameInfo) <= 3050) ? 1 : -1];
char sizecheck_DNSServer [(sizeof(DNSServer) <= 320) ? 1 : -1];
- char sizecheck_NetworkInterfaceInfo[(sizeof(NetworkInterfaceInfo) <= 6750) ? 1 : -1];
+ char sizecheck_NetworkInterfaceInfo[(sizeof(NetworkInterfaceInfo) <= 6850) ? 1 : -1];
char sizecheck_ServiceRecordSet [(sizeof(ServiceRecordSet) <= 5500) ? 1 : -1];
- char sizecheck_DomainAuthInfo [(sizeof(DomainAuthInfo) <= 7550) ? 1 : -1];
- char sizecheck_ServiceInfoQuery [(sizeof(ServiceInfoQuery) <= 3090) ? 1 : -1];
+ char sizecheck_DomainAuthInfo [(sizeof(DomainAuthInfo) <= 7808) ? 1 : -1];
+ char sizecheck_ServiceInfoQuery [(sizeof(ServiceInfoQuery) <= 3200) ? 1 : -1];
#if APPLE_OSX_mDNSResponder
- char sizecheck_ClientTunnel [(sizeof(ClientTunnel) <= 1104) ? 1 : -1];
+ char sizecheck_ClientTunnel [(sizeof(ClientTunnel) <= 1148) ? 1 : -1];
#endif
};
* Any dynamic run-time requirements should be handled by the platform layer below or client layer above
*/
+#if APPLE_OSX_mDNSResponder
+#include <TargetConditionals.h>
+#endif
#include "uDNS.h"
#if(defined(_MSC_VER))
#pragma mark - Name Server List Management
#endif
-mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped)
+mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout)
{
DNSServer **p = &m->DNSServers;
DNSServer *tmp = mDNSNULL;
(*p)->flags = DNSServer_FlagNew;
(*p)->teststate = /* DNSServer_Untested */ DNSServer_Passed;
(*p)->lasttest = m->timenow - INIT_UCAST_POLL_INTERVAL;
+ (*p)->timeout = timeout;
AssignDomainName(&(*p)->domain, d);
(*p)->next = mDNSNULL;
}
// MUST be called with the lock held
mDNSexport mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info,
- const domainname *domain, const domainname *keyname, const char *b64keydata, mDNSBool AutoTunnel)
+ const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, const char *autoTunnelPrefix)
{
DNSQuestion *q;
DomainAuthInfo **p = &m->AuthInfoList;
if (!info || !b64keydata) { LogMsg("mDNS_SetSecretForDomain: ERROR: info %p b64keydata %p", info, b64keydata); return(mStatus_BadParamErr); }
- LogInfo("mDNS_SetSecretForDomain: domain %##s key %##s%s", domain->c, keyname->c, AutoTunnel ? " AutoTunnel" : "");
+ LogInfo("mDNS_SetSecretForDomain: domain %##s key %##s%s%s", domain->c, keyname->c, autoTunnelPrefix ? " prefix " : "", autoTunnelPrefix ? autoTunnelPrefix : "");
- info->AutoTunnel = AutoTunnel;
+ info->AutoTunnel = autoTunnelPrefix;
AssignDomainName(&info->domain, domain);
AssignDomainName(&info->keyname, keyname);
+ if (hostname)
+ AssignDomainName(&info->hostname, hostname);
+ else
+ info->hostname.c[0] = 0;
+ if (port)
+ info->port = *port;
+ else
+ info->port = zeroIPPort;
mDNS_snprintf(info->b64keydata, sizeof(info->b64keydata), "%s", b64keydata);
if (DNSDigest_ConstructHMACKeyfromBase64(info, b64keydata) < 0)
// !!!KRS implement me
// format opt rr (fields not specified are zero-valued)
- mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
+ mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
opt->rrclass = NormalMaxDNSMessageData;
opt->rdlength = sizeof(rdataOPT); // One option in this OPT record
opt->rdestimate = sizeof(rdataOPT);
zd->question.ForceMCast = mDNSfalse;
zd->question.ReturnIntermed = mDNStrue;
zd->question.SuppressUnusable = mDNSfalse;
- zd->question.WakeOnResolve = mDNSfalse;
+ zd->question.SearchListIndex = 0;
+ zd->question.AppendSearchDomains = 0;
+ zd->question.RetryWithSearchDomains = mDNSfalse;
+ zd->question.TimeoutQuestion = 0;
+ zd->question.WakeOnResolve = 0;
+ zd->question.qnameOrig = mDNSNULL;
zd->question.QuestionCallback = GetZoneData_QuestionCallback;
zd->question.QuestionContext = zd;
zd->ZoneDataContext = ZoneDataContext;
zd->question.QuestionContext = zd;
- AssignDomainName(&zd->question.qname, zd->CurrentSOA);
mDNS_DropLockBeforeCallback(); // GetZoneData_StartQuery expects to be called from a normal callback, so we emulate that here
- GetZoneData_StartQuery(m, zd, kDNSType_SOA);
+ if (AuthInfo && AuthInfo->AutoTunnel && !mDNSIPPortIsZero(AuthInfo->port))
+ {
+ LogInfo("StartGetZoneData: Bypassing SOA, SRV query for %##s", AuthInfo->domain.c);
+ // We bypass SOA and SRV queries if we know the hostname and port already from the configuration.
+ // Today this is only true for AutoTunnel. As we bypass, we need to infer a few things:
+ //
+ // 1. Zone name is the same as the AuthInfo domain
+ // 2. ZoneClass is kDNSClass_IN which should be a safe assumption
+ //
+ // If we want to make this bypass mechanism work for non-AutoTunnels also, (1) has to hold
+ // good. Otherwise, it has to be configured also.
+
+ AssignDomainName(&zd->ZoneName, &AuthInfo->domain);
+ zd->ZoneClass = kDNSClass_IN;
+ AssignDomainName(&zd->Host, &AuthInfo->hostname);
+ zd->Port = AuthInfo->port;
+ AssignDomainName(&zd->question.qname, &zd->Host);
+ GetZoneData_StartQuery(m, zd, kDNSType_A);
+ }
+ else
+ {
+ if (AuthInfo && AuthInfo->AutoTunnel) LogInfo("StartGetZoneData: Not Bypassing SOA, SRV query for %##s", AuthInfo->domain.c);
+ AssignDomainName(&zd->question.qname, zd->CurrentSOA);
+ GetZoneData_StartQuery(m, zd, kDNSType_SOA);
+ }
mDNS_ReclaimLockAfterCallback();
return zd;
}
+// Returns if the question is a GetZoneData question. These questions are special in
+// that they are created internally while resolving a private query or LLQs.
+mDNSexport mDNSBool IsGetZoneDataQuestion(DNSQuestion *q)
+ {
+ if (q->QuestionCallback == GetZoneData_QuestionCallback) return(mDNStrue);
+ else return(mDNSfalse);
+ }
+
// GetZoneData queries are a special case -- even if we have a key for them, we don't do them privately,
// because that would result in an infinite loop (i.e. to do a private query we first need to get
// the _dns-query-tls SRV record for the zone, and we can't do *that* privately because to do so
}
else
{
- // Clearing SRVchanged is a safety measure. If our pewvious dereg never
+ // Clearing SRVchanged is a safety measure. If our pevious dereg never
// came back and we had a target change, we are starting fresh
r->SRVChanged = mDNSfalse;
// if it is already registered or in the process of registering, then don't
{
if (!mDNSIPv4AddressIsZero(m->AdvertisedV4.ip.v4) && h->arv4.resrec.RecordType == kDNSRecordTypeUnregistered)
{
- mDNS_SetupResourceRecord(&h->arv4, mDNSNULL, mDNSInterface_Any, kDNSType_A, kHostNameTTL, kDNSRecordTypeUnregistered, HostnameCallback, h);
+ mDNS_SetupResourceRecord(&h->arv4, mDNSNULL, mDNSInterface_Any, kDNSType_A, kHostNameTTL, kDNSRecordTypeUnregistered, AuthRecordAny, HostnameCallback, h);
AssignDomainName(&h->arv4.namestorage, &h->fqdn);
h->arv4.resrec.rdata->u.ipv4 = m->AdvertisedV4.ip.v4;
h->arv4.state = regState_Unregistered;
if (!mDNSIPv6AddressIsZero(m->AdvertisedV6.ip.v6) && h->arv6.resrec.RecordType == kDNSRecordTypeUnregistered)
{
- mDNS_SetupResourceRecord(&h->arv6, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL, kDNSRecordTypeKnownUnique, HostnameCallback, h);
+ mDNS_SetupResourceRecord(&h->arv6, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, HostnameCallback, h);
AssignDomainName(&h->arv6.namestorage, &h->fqdn);
h->arv6.resrec.rdata->u.ipv6 = m->AdvertisedV6.ip.v6;
h->arv6.state = regState_Unregistered;
q->ForceMCast = mDNSfalse;
q->ReturnIntermed = mDNStrue;
q->SuppressUnusable = mDNSfalse;
- q->WakeOnResolve = mDNSfalse;
+ q->SearchListIndex = 0;
+ q->AppendSearchDomains = 0;
+ q->RetryWithSearchDomains = mDNSfalse;
+ q->TimeoutQuestion = 0;
+ q->WakeOnResolve = 0;
+ q->qnameOrig = mDNSNULL;
q->QuestionCallback = FoundStaticHostname;
q->QuestionContext = mDNSNULL;
}
spaceleft -= rrSize;
oldnext = next;
- LogInfo("SendGroupUpdates: Building a message with resource record %s, next %p, state %d", ARDisplayString(m, rr), next, rr->state);
+ LogInfo("SendGroupUpdates: Building a message with resource record %s, next %p, state %d, ttl %d", ARDisplayString(m, rr), next, rr->state, rr->resrec.rroriginalttl);
if (!(next = BuildUpdateMessage(m, next, rr, limit)))
{
// We calculated the space and if we can't fit in, we had some bug in the calculation,
(const mDNSu8 *)"\002lb",
(const mDNSu8 *)"\001r",
(const mDNSu8 *)"\002dr",
- (const mDNSu8 *)"\002cf",
(const mDNSu8 *)mDNSNULL,
};
}
}
-mDNSexport void mDNS_AddSearchDomain(const domainname *const domain)
+mDNSexport void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID)
{
SearchListElem **p;
+ SearchListElem *tmp = mDNSNULL;
// Check to see if we already have this domain in our list
for (p = &SearchList; *p; p = &(*p)->next)
- if (SameDomainName(&(*p)->domain, domain))
+ if (((*p)->InterfaceID == InterfaceID) && SameDomainName(&(*p)->domain, domain))
{
// If domain is already in list, and marked for deletion, unmark the delete
// Be careful not to touch the other flags that may be present
- if ((*p)->flag & SLE_DELETE) (*p)->flag &= ~SLE_DELETE;
LogInfo("mDNS_AddSearchDomain already in list %##s", domain->c);
- return;
+ if ((*p)->flag & SLE_DELETE) (*p)->flag &= ~SLE_DELETE;
+ tmp = *p;
+ *p = tmp->next;
+ tmp->next = mDNSNULL;
+ break;
}
- // if domain not in list, add to list, mark as add (1)
- *p = mDNSPlatformMemAllocate(sizeof(SearchListElem));
- if (!*p) { LogMsg("ERROR: mDNS_AddSearchDomain - malloc"); return; }
- mDNSPlatformMemZero(*p, sizeof(SearchListElem));
- AssignDomainName(&(*p)->domain, domain);
- (*p)->next = mDNSNULL;
- LogInfo("mDNS_AddSearchDomain created new %##s", domain->c);
+
+ // move to end of list so that we maintain the same order
+ while (*p) p = &(*p)->next;
+
+ if (tmp) *p = tmp;
+ else
+ {
+ // if domain not in list, add to list, mark as add (1)
+ *p = mDNSPlatformMemAllocate(sizeof(SearchListElem));
+ if (!*p) { LogMsg("ERROR: mDNS_AddSearchDomain - malloc"); return; }
+ mDNSPlatformMemZero(*p, sizeof(SearchListElem));
+ AssignDomainName(&(*p)->domain, domain);
+ (*p)->next = mDNSNULL;
+ (*p)->InterfaceID = InterfaceID;
+ LogInfo("mDNS_AddSearchDomain created new %##s, InterfaceID %p", domain->c, InterfaceID);
+ }
}
mDNSlocal void FreeARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
if (result == mStatus_MemFree) mDNSPlatformMemFree(rr->RecordContext);
}
-#if APPLE_OSX_mDNSResponder
-mDNSlocal void CheckAutoTunnel6Registration(mDNS *const m, mDNSBool RegisterAutoTunnel6)
- {
- LogInfo("CheckAutoTunnel6Registration: Current value RegisterAutoTunnel6 %d, New value %d", m->RegisterAutoTunnel6, RegisterAutoTunnel6);
- if (!RegisterAutoTunnel6)
- {
- // We are not supposed to register autotunnel6. If we had previously registered
- // autotunnel6, deregister it now.
- if (m->RegisterAutoTunnel6)
- {
- m->RegisterAutoTunnel6 = mDNSfalse;
- LogInfo("CheckAutoTunnel6Registration: Removing AutoTunnel6");
- RemoveAutoTunnel6Record(m);
- }
- else LogInfo("CheckAutoTunnel6Registration: Already Removed AutoTunnel6");
- }
- else
- {
- // We are supposed to register autotunnel6. If we had previously de-registered
- // autotunnel6, re-register it now.
- if (!m->RegisterAutoTunnel6)
- {
- m->RegisterAutoTunnel6 = mDNStrue;
- LogInfo("CheckAutoTunnel6Registration: Adding AutoTunnel6");
- SetupConndConfigChanges(m);
- }
- else LogInfo("CheckAutoTunnel6Registration: already Added AutoTunnel6");
- }
- }
-#endif
-
-mDNSlocal void FoundCFDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
- {
- SearchListElem *slElem = question->QuestionContext;
- mDNSBool RegisterAutoTunnel6 = mDNStrue;
- char *res = "DisableInboundRelay";
-
- LogInfo("FoundCFDomain: InterfaceID %p %s Question %##s Answer %s", answer->InterfaceID, AddRecord ? "Add" : "Rmv", question->qname.c, RRDisplayString(m, answer));
- if (answer->rrtype != kDNSType_TXT)
- {
- LogMsg("FoundCFDomain: answer type is not TXT %s for question %##s", DNSTypeName(answer->rrtype), question->qname.c);
- return;
- }
- if (answer->RecordType == kDNSRecordTypePacketNegative)
- {
- LogInfo("FoundCFDomain: Negative answer for %##s", question->qname.c);
- return;
- }
- if (answer->InterfaceID == mDNSInterface_LocalOnly)
- {
- LogInfo("FoundCFDomain: LocalOnly interfaceID for %##s", question->qname.c);
- return;
- }
-
- // TXT record is encoded as <len><data>
- if (answer->rdlength != mDNSPlatformStrLen(res) + 1)
- {
- LogInfo("FoundCFDomain: Invalid TXT record to disable %##s, length %d", question->qname.c, answer->rdlength);
- return;
- }
-
- // Compare the data (excluding the len byte)
- if (!mDNSPlatformMemSame(&answer->rdata->u.txt.c[1], res, answer->rdlength - 1))
- {
- LogInfo("FoundCFDomain: Invalid TXT record to disable %##s", question->qname.c);
- return;
- }
-
- // It is sufficient for one answer to disable registration of autotunnel6. But we should
- // have zero answers across all domains to register autotunnel6.
- if (AddRecord)
- {
- slElem->numCfAnswers++;
- RegisterAutoTunnel6 = mDNSfalse;
- }
- else
- {
- const SearchListElem *s;
- slElem->numCfAnswers--;
- if (slElem->numCfAnswers < 0) LogMsg("FoundCFDomain: numCfAnswers less than zero %d", slElem->numCfAnswers);
- // See if any domain (including the slElem) has any answers
- for (s=SearchList; s; s=s->next)
- if (s->numCfAnswers) { RegisterAutoTunnel6 = mDNSfalse; break; }
- }
-#if APPLE_OSX_mDNSResponder
- CheckAutoTunnel6Registration(m, RegisterAutoTunnel6);
-#endif
- }
-
mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
{
SearchListElem *slElem = question->QuestionContext;
{
ARListElem *arElem = mDNSPlatformMemAllocate(sizeof(ARListElem));
if (!arElem) { LogMsg("ERROR: FoundDomain out of memory"); return; }
- mDNS_SetupResourceRecord(&arElem->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, FreeARElemCallback, arElem);
+ mDNS_SetupResourceRecord(&arElem->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, AuthRecordLocalOnly, FreeARElemCallback, arElem);
MakeDomainNameFromDNSNameString(&arElem->ar.namestorage, name);
AppendDNSNameString (&arElem->ar.namestorage, "local");
AssignDomainName(&arElem->ar.resrec.rdata->u.name, &answer->rdata->u.name);
DomainAuthInfo *info;
for (info = m->AuthInfoList; info; info = info->next)
- if (info->next == (DomainAuthInfo *)~0 || info->AutoTunnel == (mDNSBool)~0)
+ if (info->next == (DomainAuthInfo *)~0 || info->AutoTunnel == (const char*)~0)
LogMemCorruption("m->AuthInfoList: %p is garbage (%X)", info, info->AutoTunnel);
HostnameInfo *hi;
}
#endif
-mDNSlocal void mDNS_StartCFQuestion(mDNS *const m, DNSQuestion *question, domainname *domain, void *context)
- {
- AssignDomainName (&question->qname, (const domainname*)"\002cf" "\007_dns-sd" "\x04_udp");
- AppendDomainName (&question->qname, domain);
- question->InterfaceID = mDNSInterface_Any;
- question->Target = zeroAddr;
- question->qtype = kDNSType_TXT;
- question->qclass = kDNSClass_IN;
- question->LongLived = mDNSfalse;
- question->ExpectUnique = mDNStrue;
- question->ForceMCast = mDNSfalse;
- question->ReturnIntermed = mDNSfalse;
- question->SuppressUnusable = mDNSfalse;
- question->WakeOnResolve = mDNSfalse;
- question->QuestionCallback = FoundCFDomain;
- question->QuestionContext = context;
- LogInfo("mDNS_StartCFQuestion: Start CF domain question %##s", question->qname.c);
- if (mDNS_StartQuery(m, question))
- LogMsg("mDNS_StartCFQuestion: ERROR!! cannot start cf._dns-sd query");
- }
-
// This should probably move to the UDS daemon -- the concept of legacy clients and automatic registration / automatic browsing
// is really a UDS API issue, not something intrinsic to uDNS
+
mDNSexport mStatus uDNS_SetupSearchDomains(mDNS *const m, int action)
{
SearchListElem **p = &SearchList, *ptr;
- const SearchListElem *s;
- mDNSBool RegisterAutoTunnel6 = mDNStrue;
mStatus err;
// step 1: mark each element for removal
for (ptr = SearchList; ptr; ptr = ptr->next) ptr->flag |= SLE_DELETE;
- // Client has requested domain enumeration or automatic browse -- time to make sure we have the search domains from the platform layer
+ // Make sure we have the search domains from the platform layer so that if we start the WAB
+ // queries below, we have the latest information
mDNS_Lock(m);
mDNSPlatformSetDNSConfig(m, mDNSfalse, mDNStrue, mDNSNULL, mDNSNULL, mDNSNULL);
mDNS_Unlock(m);
while (*p)
{
ptr = *p;
- LogInfo("uDNS_SetupSearchDomains:action %d: Flags %d, AuthRecs %p, %##s", action, ptr->flag, ptr->AuthRecs, ptr->domain.c);
+ LogInfo("uDNS_SetupSearchDomains:action %d: Flags %d, AuthRecs %p, InterfaceID %p %##s", action, ptr->flag, ptr->AuthRecs, ptr->InterfaceID, ptr->domain.c);
if (ptr->flag & SLE_DELETE)
{
ARListElem *arList = ptr->AuthRecs;
*p = ptr->next;
// If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries
- // Note: Stopping a question will not generate the RMV events for the question (handled in FoundCFDomain)
- // and hence we need to recheck all the domains to see if we need to register/deregister _autotunnel6.
- // This is done at the end.
- if ((ptr->flag & SLE_WAB_QUERY_STARTED) && !SameDomainName(&ptr->domain, &localdomain))
+ // We suppressed the domain enumeration for scoped search domains below. When we enable that
+ // enable this.
+ if ((ptr->flag & SLE_WAB_QUERY_STARTED) &&
+ !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any))
{
mDNS_StopGetDomains(m, &ptr->BrowseQ);
mDNS_StopGetDomains(m, &ptr->RegisterQ);
mDNS_StopGetDomains(m, &ptr->DefRegisterQ);
mDNS_StopGetDomains(m, &ptr->AutomaticBrowseQ);
}
-#if !TARGET_OS_EMBEDDED
- if ((ptr->flag & SLE_CF_QUERY_STARTED) && !SameDomainName(&ptr->domain, &localdomain))
- {
- mDNS_StopGetDomains(m, &ptr->CfQ);
- }
-#endif
+
mDNSPlatformMemFree(ptr);
// deregister records generated from answers to the query
if ((action & UDNS_START_WAB_QUERY) && !(ptr->flag & SLE_WAB_QUERY_STARTED))
{
// If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries.
- if (!SameDomainName(&ptr->domain, &localdomain))
+ // Also, suppress the domain enumeration for scoped search domains for now until there is a need.
+ if (!SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any))
{
mStatus err1, err2, err3, err4, err5;
- err1 = mDNS_GetDomains(m, &ptr->BrowseQ, mDNS_DomainTypeBrowse, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr);
- err2 = mDNS_GetDomains(m, &ptr->DefBrowseQ, mDNS_DomainTypeBrowseDefault, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr);
- err3 = mDNS_GetDomains(m, &ptr->RegisterQ, mDNS_DomainTypeRegistration, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr);
- err4 = mDNS_GetDomains(m, &ptr->DefRegisterQ, mDNS_DomainTypeRegistrationDefault, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr);
- err5 = mDNS_GetDomains(m, &ptr->AutomaticBrowseQ, mDNS_DomainTypeBrowseAutomatic, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr);
+ err1 = mDNS_GetDomains(m, &ptr->BrowseQ, mDNS_DomainTypeBrowse, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr);
+ err2 = mDNS_GetDomains(m, &ptr->DefBrowseQ, mDNS_DomainTypeBrowseDefault, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr);
+ err3 = mDNS_GetDomains(m, &ptr->RegisterQ, mDNS_DomainTypeRegistration, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr);
+ err4 = mDNS_GetDomains(m, &ptr->DefRegisterQ, mDNS_DomainTypeRegistrationDefault, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr);
+ err5 = mDNS_GetDomains(m, &ptr->AutomaticBrowseQ, mDNS_DomainTypeBrowseAutomatic, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr);
if (err1 || err2 || err3 || err4 || err5)
LogMsg("uDNS_SetupSearchDomains: GetDomains for domain %##s returned error(s):\n"
"%d (mDNS_DomainTypeBrowse)\n"
ptr->flag |= SLE_WAB_QUERY_STARTED;
}
}
-#if !TARGET_OS_EMBEDDED
- if ((action & UDNS_START_CF_QUERY) && !(ptr->flag & SLE_CF_QUERY_STARTED))
+
+ p = &ptr->next;
+ }
+ return mStatus_NoError;
+ }
+
+mDNSexport domainname *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal)
+ {
+ SearchListElem *p = SearchList;
+ int count = *searchIndex;
+ (void) m; // unused
+
+ if (count < 0) { LogMsg("uDNS_GetNextSearchDomain: count %d less than zero", count); return mDNSNULL; }
+
+ // skip the domains that we already looked at before
+ for (; count; count--) p = p->next;
+
+ while (p)
+ {
+ int labels = CountLabels(&p->domain);
+ if (labels > 0)
{
- if (!SameDomainName(&ptr->domain, &localdomain))
+ const domainname *d = SkipLeadingLabels(&p->domain, labels - 1);
+ if (SameDomainLabel(d->c, (const mDNSu8 *)"\x4""arpa"))
+ {
+ LogInfo("uDNS_GetNextSearchDomain: skipping search domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID);
+ (*searchIndex)++;
+ p = p->next;
+ continue;
+ }
+ if (ignoreDotLocal && SameDomainLabel(d->c, (const mDNSu8 *)"\x5""local"))
{
- mDNS_StartCFQuestion(m, &ptr->CfQ, &ptr->domain, ptr);
- ptr->flag |= SLE_CF_QUERY_STARTED;
+ LogInfo("uDNS_GetNextSearchDomain: skipping local domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID);
+ (*searchIndex)++;
+ p = p->next;
+ continue;
}
}
-#endif
+ // Point to the next one in the list which we will look at next time.
+ (*searchIndex)++;
+ // When we are appending search domains in a ActiveDirectory domain, the question's InterfaceID
+ // set to mDNSInterface_Unicast. Match the unscoped entries in that case.
+ if (((InterfaceID == mDNSInterface_Unicast) && (p->InterfaceID == mDNSInterface_Any)) ||
+ p->InterfaceID == InterfaceID)
+ {
+ LogInfo("uDNS_GetNextSearchDomain returning domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID);
+ return &p->domain;
+ }
+ LogInfo("uDNS_GetNextSearchDomain skipping domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID);
+ p = p->next;
+ }
+ return mDNSNULL;
+ }
- p = &ptr->next;
+mDNSlocal void FlushAddressCacheRecords(mDNS *const m)
+ {
+ mDNSu32 slot;
+ CacheGroup *cg;
+ CacheRecord *cr;
+ FORALL_CACHERECORDS(slot, cg, cr)
+ {
+ if (cr->resrec.InterfaceID) continue;
+
+ // If a resource record can answer A or AAAA, they need to be flushed so that we will
+ // never used to deliver an ADD or RMV
+ if (RRTypeAnswersQuestionType(&cr->resrec, kDNSType_A) ||
+ RRTypeAnswersQuestionType(&cr->resrec, kDNSType_AAAA))
+ {
+ LogInfo("FlushAddressCacheRecords: Purging Resourcerecord %s", CRDisplayString(m, cr));
+ mDNS_PurgeCacheResourceRecord(m, cr);
+ }
}
-#if !TARGET_OS_EMBEDDED
- // if there is any domain has answers, need to deregister autotunnel6
- for (s=SearchList; s; s=s->next)
- if (s->numCfAnswers) { RegisterAutoTunnel6 = mDNSfalse; break; }
-#if APPLE_OSX_mDNSResponder
- CheckAutoTunnel6Registration(m, RegisterAutoTunnel6);
-#endif
-#endif
- return mStatus_NoError;
+ }
+
+// Retry questions which has seach domains appended
+mDNSexport void RetrySearchDomainQuestions(mDNS *const m)
+ {
+ // Purge all the A/AAAA cache records and restart the queries. mDNSCoreRestartAddressQueries
+ // does this. When we restart the question, we first want to try the new search domains rather
+ // than use the entries that is already in the cache. When we appended search domains, we might
+ // have created cache entries which is no longer valid as there are new search domains now
+
+ LogInfo("RetrySearchDomainQuestions: Calling mDNSCoreRestartAddressQueries");
+ mDNSCoreRestartAddressQueries(m, mDNStrue, FlushAddressCacheRecords, mDNSNULL, mDNSNULL);
}
// Construction of Default Browse domain list (i.e. when clients pass NULL) is as follows:
// other overly-large structures instead of having a pointer to them, can inadvertently
// cause structure sizes (and therefore memory usage) to balloon unreasonably.
char sizecheck_tcpInfo_t [(sizeof(tcpInfo_t) <= 9056) ? 1 : -1];
- char sizecheck_SearchListElem[(sizeof(SearchListElem) <= 4860) ? 1 : -1];
+ char sizecheck_SearchListElem[(sizeof(SearchListElem) <= 5000) ? 1 : -1];
};
// 5 minutes
#define MAX_UPDATE_REFRESH_COUNT 5
#define MIN_UPDATE_REFRESH_TIME (5 * 60 * mDNSPlatformOneSecond)
+
+// For questions that use kDNSServiceFlagsTimeout and we don't have a matching resolver e.g., no dns servers,
+// then use the default value of 30 seconds
+#define DEFAULT_UDNS_TIMEOUT 30 // in seconds
+
// Entry points into unicast-specific routines
extern void LLQGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo);
// more values for "action" which does the following:
//
// -UDNS_START_WAB_QUERY - start Wide Area Bonjour (domain enumeration) queries
-// -UDNS_START_CF_QUERY - start Configuration query
#define UDNS_START_WAB_QUERY 0x00000001
-#define UDNS_START_CF_QUERY 0x00000002
extern mStatus uDNS_SetupSearchDomains(mDNS *const m, int action);
+extern domainname *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal);
typedef enum
{
txtbuffer, (mDNSu16)(1+txtbuffer[0]), // TXT data, length
mDNSNULL, 0, // Subtypes (none)
mDNSInterface_Any, // Interface ID
- Callback, mDNSNULL); // Callback and context
+ Callback, mDNSNULL, 0); // Callback, context, flags
ConvertDomainNameToCString(recordset->RR_SRV.resrec.name, buffer);
printf("Made Service Records for %s\n", buffer);
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>com.apple.${PRODUCT_NAME:rfc1034Identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>BNDL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFPlugInDynamicRegisterFunction</key>
+ <string></string>
+ <key>CFPlugInDynamicRegistration</key>
+ <string>NO</string>
+ <key>CFPlugInFactories</key>
+ <dict>
+ <key>FB86416D-6164-2070-726F-70735C216EC0</key>
+ <string>UserEventAgentFactory</string>
+ </dict>
+ <key>CFPlugInTypes</key>
+ <dict>
+ <key>FC86416D-6164-2070-726F-70735C216EC0</key>
+ <array>
+ <string>FB86416D-6164-2070-726F-70735C216EC0</string>
+ </array>
+ </dict>
+ <key>CFPlugInUnloadFunction</key>
+ <string></string>
+ <key>LimitLoadToSessionType</key>
+ <array>
+ <string>System</string>
+ <string>Aqua</string>
+ </array>
+</dict>
+</plist>
--- /dev/null
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <dns_sd.h>
+#include <UserEventAgentInterface.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+
+#pragma mark -
+#pragma mark Types
+#pragma mark -
+static const char* sPluginIdentifier = "com.apple.bonjour.events";
+
+// PLIST Keys
+static const CFStringRef sServiceNameKey = CFSTR("ServiceName");
+static const CFStringRef sServiceTypeKey = CFSTR("ServiceType");
+static const CFStringRef sServiceDomainKey = CFSTR("ServiceDomain");
+
+static const CFStringRef sOnServiceAddKey = CFSTR("OnServiceAdd");
+static const CFStringRef sOnServiceRemoveKey = CFSTR("OnServiceRemove");
+static const CFStringRef sWhileServiceExistsKey = CFSTR("WhileServiceExists");
+
+static const CFStringRef sLaunchdTokenKey = CFSTR("LaunchdToken");
+
+static const CFStringRef sPluginTimersKey = CFSTR("PluginTimers");
+
+
+/************************************************
+ * Launch Event Dictionary (input from launchd)
+ * Passed To: ManageEventsCallback
+ *-----------------------------------------------
+ * Typing in this dictionary is not enforced
+ * above us. So this may not be true. Type check
+ * all input before using it.
+ *-----------------------------------------------
+ * sServiceNameKey - CFString (Optional)
+ * sServiceTypeKey - CFString
+ * sServiceDomainKey - CFString
+ *
+ * One or more of the following.
+ *-----------------------------------
+ * sOnServiceAddKey - CFBoolean
+ * sOnServiceRemoveKey - CFBoolean
+ * sWhileServiceExistsKey - CFBoolean
+ ************************************************/
+
+/************************************************
+ * Browser Dictionary
+ *-----------------------------------------------
+ * sServiceDomainKey - CFString
+ * sServiceTypeKey - CFString
+ ************************************************/
+
+/************************************************
+ * Event Dictionary
+ *-----------------------------------------------
+ * sServiceNameKey - CFString (Optional)
+ * sLaunchdTokenKey - CFNumber
+ ************************************************/
+
+typedef struct {
+ UserEventAgentInterfaceStruct* _UserEventAgentInterface;
+ CFUUIDRef _factoryID;
+ UInt32 _refCount;
+
+ void* _pluginContext;
+
+ CFMutableDictionaryRef _tokenToBrowserMap; // Maps a token to a browser that can be used to scan the remaining dictionaries.
+ CFMutableDictionaryRef _browsers; // A Dictionary of "Browser Dictionarys" where the resposible browser is the key.
+ CFMutableDictionaryRef _onAddEvents; // A Dictionary of "Event Dictionarys" that describe events to trigger on a service appearing.
+ CFMutableDictionaryRef _onRemoveEvents; // A Dictionary of "Event Dictionarys" that describe events to trigger on a service disappearing.
+ CFMutableDictionaryRef _whileServiceExist; // A Dictionary of "Event Dictionarys" that describe events to trigger on a service disappearing.
+
+
+ CFMutableArrayRef _timers;
+
+} BonjourUserEventsPlugin;
+
+
+typedef struct {
+
+ CFIndex refCount;
+ BonjourUserEventsPlugin* plugin;
+ CFNumberRef token;
+
+} TimerContextInfo;
+
+typedef struct {
+ CFIndex refCount;
+ DNSServiceRef browserRef;
+} NetBrowserInfo;
+
+#pragma mark -
+#pragma mark Prototypes
+#pragma mark -
+// COM Stuff
+static HRESULT QueryInterface(void *myInstance, REFIID iid, LPVOID *ppv);
+static ULONG AddRef(void* instance);
+static ULONG Release(void* instance);
+
+static BonjourUserEventsPlugin* Alloc(CFUUIDRef factoryID);
+static void Dealloc(BonjourUserEventsPlugin* plugin);
+
+void * UserEventAgentFactory(CFAllocatorRef allocator, CFUUIDRef typeID);
+
+// Plugin Management
+static void Install(void* instance);
+static void ManageEventsCallback(
+ UserEventAgentLaunchdAction action,
+ CFNumberRef token,
+ CFTypeRef eventMatchDict,
+ void * vContext);
+
+
+// Plugin Guts
+void AddEventToPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchdToken, CFDictionaryRef eventParameters);
+void RemoveEventFromPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchToken);
+
+NetBrowserInfo* CreateBrowserForTypeAndDomain(BonjourUserEventsPlugin* plugin, CFStringRef type, CFStringRef domain);
+NetBrowserInfo* BrowserForSDRef(BonjourUserEventsPlugin* plugin, DNSServiceRef sdRef);
+void AddEventDictionary(CFDictionaryRef eventDict, CFMutableDictionaryRef allEventsDictionary, NetBrowserInfo* key);
+void RemoveEventFromArray(CFMutableArrayRef array, CFNumberRef launchdToken);
+
+// Net Service Browser Stuff
+void ServiceBrowserCallback (DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char* serviceName, const char* regtype, const char* replyDomain, void* context);
+void HandleTemporaryEventsForService(BonjourUserEventsPlugin* plugin, NetBrowserInfo* browser, CFStringRef serviceName, CFMutableDictionaryRef eventsDictionary);
+void HandleStateEventsForService(BonjourUserEventsPlugin* plugin, NetBrowserInfo* browser, CFStringRef serviceName, Boolean didAppear);
+void TemporaryEventTimerCallout ( CFRunLoopTimerRef timer, void *info );
+
+// Convence Stuff
+const char* CStringFromCFString(CFStringRef string);
+
+
+// TimerContextInfo "Object"
+TimerContextInfo* TimerContextInfoCreate(BonjourUserEventsPlugin* plugin, CFNumberRef token);
+const void* TimerContextInfoRetain(const void* info);
+void TimerContextInfoRelease(const void* info);
+CFStringRef TimerContextInfoCopyDescription(const void* info);
+
+// NetBrowserInfo "Object"
+NetBrowserInfo* NetBrowserInfoCreate(CFStringRef serviceType, CFStringRef domain, void* context);
+const void* NetBrowserInfoRetain(CFAllocatorRef allocator, const void* info);
+void NetBrowserInfoRelease(CFAllocatorRef allocator, const void* info);
+Boolean NetBrowserInfoEqual(const void *value1, const void *value2);
+CFHashCode NetBrowserInfoHash(const void *value);
+CFStringRef NetBrowserInfoCopyDescription(const void *value);
+
+
+static const CFDictionaryKeyCallBacks kNetBrowserInfoDictionaryKeyCallbacks = {
+ 0,
+ NetBrowserInfoRetain,
+ NetBrowserInfoRelease,
+ NetBrowserInfoCopyDescription,
+ NetBrowserInfoEqual,
+ NetBrowserInfoHash
+};
+
+static const CFDictionaryValueCallBacks kNetBrowserInfoDictionaryValueCallbacks = {
+ 0,
+ NetBrowserInfoRetain,
+ NetBrowserInfoRelease,
+ NetBrowserInfoCopyDescription,
+ NetBrowserInfoEqual
+};
+
+// COM type definition goop.
+static UserEventAgentInterfaceStruct UserEventAgentInterfaceFtbl = {
+ NULL, // Required padding for COM
+ QueryInterface, // Query Interface
+ AddRef, // AddRef()
+ Release, // Release()
+ Install // Install
+};
+
+#pragma mark -
+#pragma mark COM Management
+#pragma mark -
+
+/*****************************************************************************
+ *****************************************************************************/
+static HRESULT QueryInterface(void *myInstance, REFIID iid, LPVOID *ppv)
+{
+ CFUUIDRef interfaceID = CFUUIDCreateFromUUIDBytes(NULL, iid);
+
+ // Test the requested ID against the valid interfaces.
+ if(CFEqual(interfaceID, kUserEventAgentInterfaceID))
+ {
+ ((BonjourUserEventsPlugin *) myInstance)->_UserEventAgentInterface->AddRef(myInstance);
+ *ppv = myInstance;
+ CFRelease(interfaceID);
+ return S_OK;
+ }
+ else if(CFEqual(interfaceID, IUnknownUUID))
+ {
+ ((BonjourUserEventsPlugin *) myInstance)->_UserEventAgentInterface->AddRef(myInstance);
+ *ppv = myInstance;
+ CFRelease(interfaceID);
+ return S_OK;
+ }
+ else // Requested interface unknown, bail with error.
+ {
+ *ppv = NULL;
+ CFRelease(interfaceID);
+ return E_NOINTERFACE;
+ }
+}
+
+/*****************************************************************************
+ *****************************************************************************/
+static ULONG AddRef(void* instance)
+{
+ BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)instance;
+ return ++plugin->_refCount;
+}
+
+/*****************************************************************************
+ *****************************************************************************/
+static ULONG Release(void* instance)
+{
+ BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)instance;
+
+ if (plugin->_refCount != 0)
+ --plugin->_refCount;
+
+ if (plugin->_refCount == 0)
+ {
+ Dealloc(instance);
+ return 0;
+ }
+
+ return plugin->_refCount;
+}
+
+/*****************************************************************************
+ * Alloc
+ * -
+ * Functionas as both +[alloc] and -[init] for the plugin. Add any
+ * initalization of member variables here.
+ *****************************************************************************/
+static BonjourUserEventsPlugin* Alloc(CFUUIDRef factoryID)
+{
+ BonjourUserEventsPlugin* plugin = malloc(sizeof(BonjourUserEventsPlugin));
+
+ plugin->_UserEventAgentInterface = &UserEventAgentInterfaceFtbl;
+ plugin->_pluginContext = NULL;
+
+ if (factoryID)
+ {
+ plugin->_factoryID = (CFUUIDRef)CFRetain(factoryID);
+ CFPlugInAddInstanceForFactory(factoryID);
+ }
+
+ plugin->_refCount = 1;
+ plugin->_tokenToBrowserMap = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kNetBrowserInfoDictionaryValueCallbacks);
+ plugin->_browsers = CFDictionaryCreateMutable(NULL, 0, &kNetBrowserInfoDictionaryKeyCallbacks, &kCFTypeDictionaryValueCallBacks);
+ plugin->_onAddEvents = CFDictionaryCreateMutable(NULL, 0, &kNetBrowserInfoDictionaryKeyCallbacks, &kCFTypeDictionaryValueCallBacks);
+ plugin->_onRemoveEvents = CFDictionaryCreateMutable(NULL, 0, &kNetBrowserInfoDictionaryKeyCallbacks, &kCFTypeDictionaryValueCallBacks);
+ plugin->_whileServiceExist = CFDictionaryCreateMutable(NULL, 0, &kNetBrowserInfoDictionaryKeyCallbacks, &kCFTypeDictionaryValueCallBacks);
+
+ plugin->_timers = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+
+ return plugin;
+}
+
+/*****************************************************************************
+ * Dealloc
+ * -
+ * Much like Obj-C dealloc this method is responsible for releasing any object
+ * this plugin is holding. Unlike ObjC, you call directly free() instead of
+ * [super dalloc].
+ *****************************************************************************/
+static void Dealloc(BonjourUserEventsPlugin* plugin)
+{
+ CFUUIDRef factoryID = plugin->_factoryID;
+
+ if (factoryID)
+ {
+ CFPlugInRemoveInstanceForFactory(factoryID);
+ CFRelease(factoryID);
+ }
+
+ if (plugin->_tokenToBrowserMap)
+ CFRelease(plugin->_tokenToBrowserMap);
+
+ if (plugin->_browsers)
+ CFRelease(plugin->_browsers);
+
+ if (plugin->_onAddEvents)
+ CFRelease(plugin->_onAddEvents);
+
+ if (plugin->_onRemoveEvents)
+ CFRelease(plugin->_onRemoveEvents);
+
+ if (plugin->_whileServiceExist)
+ CFRelease(plugin->_whileServiceExist);
+
+ if (plugin->_timers)
+ {
+ CFIndex i;
+ CFIndex count = CFArrayGetCount(plugin->_timers);
+ CFRunLoopRef crl = CFRunLoopGetCurrent();
+
+ for (i = 0; i < count; ++i)
+ {
+ CFRunLoopTimerRef timer = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(plugin->_timers, i);
+ CFRunLoopRemoveTimer(crl, timer, kCFRunLoopCommonModes);
+ }
+
+ CFRelease(plugin->_timers);
+ }
+
+ free(plugin);
+}
+
+/*******************************************************************************
+ *******************************************************************************/
+void * UserEventAgentFactory(CFAllocatorRef allocator, CFUUIDRef typeID)
+{
+ (void)allocator;
+ BonjourUserEventsPlugin * result = NULL;
+
+ if (typeID && CFEqual(typeID, kUserEventAgentTypeID)) {
+ result = Alloc(kUserEventAgentFactoryID);
+ }
+
+ return (void *)result;
+}
+
+#pragma mark -
+#pragma mark Plugin Management
+#pragma mark -
+/*****************************************************************************
+ * Install
+ * -
+ * This is invoked once when the plugin is loaded to do initial setup and
+ * allow us to register with launchd. If UserEventAgent crashes, the plugin
+ * will need to be reloaded, and hence this will get invoked again.
+ *****************************************************************************/
+static void Install(void *instance)
+{
+ BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)instance;
+
+ plugin->_pluginContext = UserEventAgentRegisterForLaunchEvents(sPluginIdentifier, &ManageEventsCallback, plugin);
+
+ if (!plugin->_pluginContext)
+ {
+ fprintf(stderr, "%s: failed to register for launch events.\n", sPluginIdentifier);
+ return;
+ }
+
+}
+
+/*****************************************************************************
+ * ManageEventsCallback
+ * -
+ * This is invoked when launchd loads a event dictionary and needs to inform
+ * us what a daemon / agent is looking for.
+ *****************************************************************************/
+static void ManageEventsCallback(UserEventAgentLaunchdAction action, CFNumberRef token, CFTypeRef eventMatchDict, void* vContext)
+{
+
+ if (!eventMatchDict || CFGetTypeID(eventMatchDict) != CFDictionaryGetTypeID())
+ {
+ fprintf(stderr, "%s given non-dictionary for event dictionary\n", sPluginIdentifier);
+ return;
+ }
+
+ if (action == kUserEventAgentLaunchdAdd)
+ {
+ // Launchd wants us to add a launch event for this token and matching dictionary.
+ AddEventToPlugin((BonjourUserEventsPlugin*)vContext, token, (CFDictionaryRef)eventMatchDict);
+ }
+ else if (action == kUserEventAgentLaunchdRemove)
+ {
+ // Launchd wants us to remove the event hook we setup for this token / matching dictionary.
+ RemoveEventFromPlugin((BonjourUserEventsPlugin*)vContext, token);
+ }
+ else
+ {
+ fprintf(stderr, "%s got unknown UserEventAction: %d\n", sPluginIdentifier, action);
+ }
+}
+
+
+#pragma mark -
+#pragma mark Plugin Guts
+#pragma mark -
+
+/*****************************************************************************
+ * AddEventToPlugin
+ * -
+ * This method is invoked when launchd wishes the plugin to setup a launch
+ * event matching the parameters in the dictionary.
+ *****************************************************************************/
+void AddEventToPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchdToken, CFDictionaryRef eventParameters)
+{
+ CFStringRef domain = CFDictionaryGetValue(eventParameters, sServiceDomainKey);
+ CFStringRef type = CFDictionaryGetValue(eventParameters, sServiceTypeKey);
+ CFStringRef name = CFDictionaryGetValue(eventParameters, sServiceNameKey);
+ CFBooleanRef cfOnAdd = CFDictionaryGetValue(eventParameters, sOnServiceAddKey);
+ CFBooleanRef cfOnRemove = CFDictionaryGetValue(eventParameters, sOnServiceRemoveKey);
+ CFBooleanRef cfWhileSericeExists = CFDictionaryGetValue(eventParameters, sWhileServiceExistsKey);
+
+ Boolean onAdd = false;
+ Boolean onRemove = false;
+ Boolean whileExists = false;
+
+ if (cfOnAdd && CFGetTypeID(cfOnRemove) == CFBooleanGetTypeID() && CFBooleanGetValue(cfOnAdd))
+ onAdd = true;
+
+ if (cfOnRemove && CFGetTypeID(cfOnRemove) == CFBooleanGetTypeID() && CFBooleanGetValue(cfOnRemove))
+ onRemove = true;
+
+ if (cfWhileSericeExists && CFGetTypeID(cfWhileSericeExists) == CFBooleanGetTypeID() && CFBooleanGetValue(cfWhileSericeExists))
+ whileExists = true;
+
+ // A type is required. If none is specified, BAIL
+ if (!type || CFGetTypeID(type) != CFStringGetTypeID())
+ {
+ fprintf(stderr, "%s, a LaunchEvent is missing a service type.\n", sPluginIdentifier);
+ return;
+ }
+
+ // If we aren't suppose to launch on services appearing or disappearing, this service does nothing. Ignore.
+ if ((!onAdd && !onRemove && !whileExists) || (onAdd && onRemove && whileExists))
+ {
+ fprintf(stderr, "%s, a LaunchEvent is missing both onAdd/onRemove/existance or has both.\n", sPluginIdentifier);
+ return;
+ }
+
+ // If no domain is specified, assume local.
+ if (!domain)
+ {
+ domain = CFSTR("local");
+ }
+ else if (CFGetTypeID(domain) != CFStringGetTypeID() ) // If the domain is not a string, fai;
+ {
+ fprintf(stderr, "%s, a LaunchEvent has a domain that is not a string.\n", sPluginIdentifier);
+ return;
+ }
+
+
+ // If we have a name filter, but it's not a string. This event it broken, bail.
+ if (name && CFGetTypeID(name) != CFStringGetTypeID())
+ {
+ fprintf(stderr, "%s, a LaunchEvent has a domain that is not a string.\n", sPluginIdentifier);
+ return;
+ }
+
+ // Get us a browser
+ NetBrowserInfo* browser = CreateBrowserForTypeAndDomain(plugin, type, domain);
+
+ if (!browser)
+ {
+ fprintf(stderr, "%s, a LaunchEvent has a domain that is not a string.\n", sPluginIdentifier);
+ return;
+ }
+
+ // Create Event Dictionary
+ CFMutableDictionaryRef eventDictionary = CFDictionaryCreateMutable(NULL, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+
+ CFDictionarySetValue(eventDictionary, sLaunchdTokenKey, launchdToken);
+
+ if (name)
+ CFDictionarySetValue(eventDictionary, sServiceNameKey, name);
+
+ // Add to the correct dictionary.
+ if (onAdd)
+ AddEventDictionary(eventDictionary, plugin->_onAddEvents, browser);
+
+ if (onRemove)
+ AddEventDictionary(eventDictionary, plugin->_onRemoveEvents, browser);
+
+ if (whileExists)
+ AddEventDictionary(eventDictionary, plugin->_whileServiceExist, browser);
+
+ // Add Token Mapping
+ CFDictionarySetValue(plugin->_tokenToBrowserMap, launchdToken, browser);
+
+ // Release Memory
+ CFRelease(eventDictionary);
+ NetBrowserInfoRelease(NULL, browser);
+
+}
+
+
+
+/*****************************************************************************
+ * RemoveEventFromPlugin
+ * -
+ * This method is invoked when launchd wishes the plugin to setup a launch
+ * event matching the parameters in the dictionary.
+ *****************************************************************************/
+void RemoveEventFromPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchdToken)
+{
+ NetBrowserInfo* browser = (NetBrowserInfo*)CFDictionaryGetValue(plugin->_tokenToBrowserMap, launchdToken);
+ Boolean othersUsingBrowser = false;
+
+ if (!browser)
+ {
+ long long value = 0;
+ CFNumberGetValue(launchdToken, kCFNumberLongLongType, &value);
+ fprintf(stderr, "%s, Launchd asked us to remove a token we did not register!\nToken:%lld\n", sPluginIdentifier, value);
+ return;
+ }
+
+ CFMutableArrayRef onAddEvents = (CFMutableArrayRef)CFDictionaryGetValue(plugin->_onAddEvents, browser);
+ CFMutableArrayRef onRemoveEvents = (CFMutableArrayRef)CFDictionaryGetValue(plugin->_onRemoveEvents, browser);
+
+ if (onAddEvents)
+ {
+ RemoveEventFromArray(onAddEvents, launchdToken);
+
+ // Is the array now empty, clean up
+ if (CFArrayGetCount(onAddEvents) == 0)
+ CFDictionaryRemoveValue(plugin->_onAddEvents, browser);
+ }
+
+ if (onRemoveEvents)
+ {
+ RemoveEventFromArray(onRemoveEvents, launchdToken);
+
+ // Is the array now empty, clean up
+ if (CFArrayGetCount(onRemoveEvents) == 0)
+ CFDictionaryRemoveValue(plugin->_onRemoveEvents, browser);
+ }
+
+ // Remove ourselves from the token dictionary.
+ CFDictionaryRemoveValue(plugin->_tokenToBrowserMap, launchdToken);
+
+ // Check to see if anyone else is using this browser.
+ CFIndex i;
+ CFIndex count = CFDictionaryGetCount(plugin->_tokenToBrowserMap);
+ NetBrowserInfo** browsers = malloc(count * sizeof(NetBrowserInfo*));
+
+ // Fetch the values of the token dictionary
+ CFDictionaryGetKeysAndValues(plugin->_tokenToBrowserMap, NULL, (const void**)browsers);
+
+ for (i = 0; i < count; ++i)
+ {
+ if (NetBrowserInfoEqual(browsers[i], browser))
+ {
+ othersUsingBrowser = true;
+ break;
+ }
+ }
+
+ // If no one else is useing our browser, clean up!
+ if (!othersUsingBrowser)
+ {
+ CFDictionaryRemoveValue(plugin->_tokenToBrowserMap, launchdToken); // This triggers release and dealloc of the browser
+ }
+
+ free(browsers);
+}
+
+
+/*****************************************************************************
+ * CreateBrowserForTypeAndDomain
+ * -
+ * This method returns a NetBrowserInfo that is looking for a type of
+ * service in a domain. If no browser exists, it will create one and return it.
+ *****************************************************************************/
+NetBrowserInfo* CreateBrowserForTypeAndDomain(BonjourUserEventsPlugin* plugin, CFStringRef type, CFStringRef domain)
+{
+ CFIndex i;
+ CFIndex count = CFDictionaryGetCount(plugin->_browsers);
+ NetBrowserInfo* browser = NULL;
+ CFDictionaryRef* dicts = malloc(count * sizeof(CFDictionaryRef));
+ NetBrowserInfo** browsers = malloc(count * sizeof(NetBrowserInfo*));
+
+ // Fetch the values of the browser dictionary
+ CFDictionaryGetKeysAndValues(plugin->_browsers, (const void**)browsers, (const void**)dicts);
+
+ // Loop thru the browsers list and see if we can find a matching one.
+ for (i = 0; i < count; ++i)
+ {
+ CFDictionaryRef browserDict = dicts[i];
+
+ CFStringRef browserType = CFDictionaryGetValue(browserDict, sServiceTypeKey);
+ CFStringRef browserDomain = CFDictionaryGetValue(browserDict, sServiceDomainKey);
+
+ // If we have a matching browser, break
+ if (CFStringCompare(browserType, type, kCFCompareCaseInsensitive) &&
+ CFStringCompare(browserDomain, domain, kCFCompareCaseInsensitive))
+ {
+ browser = browsers[i];
+ NetBrowserInfoRetain(NULL, browser);
+ break;
+ }
+ }
+
+ // No match found, lets create one!
+ if (!browser)
+ {
+
+ browser = NetBrowserInfoCreate(type, domain, plugin);
+
+ if (!browser)
+ {
+ fprintf(stderr, "%s, failed to search for %s.%s", sPluginIdentifier, CStringFromCFString(type) , CStringFromCFString(domain));
+ free(dicts);
+ free(browsers);
+ return NULL;
+ }
+
+ // Service browser created, lets add this to ourselves to the dictionary.
+ CFMutableDictionaryRef browserDict = CFDictionaryCreateMutable(NULL, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+ CFDictionarySetValue(browserDict, sServiceTypeKey, type);
+ CFDictionarySetValue(browserDict, sServiceDomainKey, domain);
+
+ // Add the dictionary to the browsers dictionary.
+ CFDictionarySetValue(plugin->_browsers, browser, browserDict);
+
+ // Release Memory
+ CFRelease(browserDict);
+ }
+
+ free(dicts);
+ free(browsers);
+
+ return browser;
+}
+
+/*****************************************************************************
+ * BrowserForSDRef
+ * -
+ * This method returns a NetBrowserInfo that matches the calling SDRef passed
+ * in via the callback.
+ *****************************************************************************/
+NetBrowserInfo* BrowserForSDRef(BonjourUserEventsPlugin* plugin, DNSServiceRef sdRef)
+{
+ CFIndex i;
+ CFIndex count = CFDictionaryGetCount(plugin->_browsers);
+ NetBrowserInfo* browser = NULL;
+ NetBrowserInfo** browsers = malloc(count * sizeof(NetBrowserInfo*));
+
+ // Fetch the values of the browser dictionary
+ CFDictionaryGetKeysAndValues(plugin->_browsers, (const void**)browsers, NULL);
+
+ // Loop thru the browsers list and see if we can find a matching one.
+ for (i = 0; i < count; ++i)
+ {
+ NetBrowserInfo* currentBrowser = browsers[i];
+
+ if (currentBrowser->browserRef == sdRef)
+ {
+ browser = currentBrowser;
+ break;
+ }
+ }
+
+
+ free(browsers);
+
+ return browser;
+}
+
+/*****************************************************************************
+ * AddEventDictionary
+ * -
+ * Adds a event to a browser's event dictionary
+ *****************************************************************************/
+
+void AddEventDictionary(CFDictionaryRef eventDict, CFMutableDictionaryRef allEventsDictionary, NetBrowserInfo* key)
+{
+ CFMutableArrayRef eventsForBrowser = (CFMutableArrayRef)CFDictionaryGetValue(allEventsDictionary, key);
+
+ if (!eventsForBrowser) // We have no events for this browser yet, lets add him.
+ {
+ eventsForBrowser = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+ CFDictionarySetValue(allEventsDictionary, key, eventsForBrowser);
+ }
+ else
+ {
+ CFRetain(eventsForBrowser);
+ }
+
+ CFArrayAppendValue(eventsForBrowser, eventDict);
+ CFRelease(eventsForBrowser);
+}
+
+/*****************************************************************************
+ * RemoveEventFromArray
+ * -
+ * Searches a Array of Event Dictionaries to find one with a matching launchd
+ * token and remove it.
+ *****************************************************************************/
+
+void RemoveEventFromArray(CFMutableArrayRef array, CFNumberRef launchdToken)
+{
+ CFIndex i;
+ CFIndex count = CFArrayGetCount(array);
+ // Loop thru looking for us.
+ for (i = 0; i < count; )
+ {
+ CFDictionaryRef eventDict = CFArrayGetValueAtIndex(array, i);
+ CFNumberRef token = CFDictionaryGetValue(eventDict, sLaunchdTokenKey);
+
+ if (CFEqual(token, launchdToken)) // This is the same event?
+ {
+ CFArrayRemoveValueAtIndex(array, i); // Remove the event,
+ break; // The token should only exist once, so it make no sense to continue.
+ }
+ else
+ {
+ ++i; // If it's not us, advance.
+ }
+ }
+}
+
+#pragma mark -
+#pragma mark Net Service Browser Stuff
+#pragma mark -
+
+/*****************************************************************************
+ * ServiceBrowserCallback
+ * -
+ * This method is the heart of the plugin. It's the runloop callback annoucing
+ * the appearence and disappearance of network services.
+ *****************************************************************************/
+
+void ServiceBrowserCallback (DNSServiceRef sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ const char* serviceName,
+ const char* regtype,
+ const char* replyDomain,
+ void* context )
+{
+ (void)interfaceIndex;
+ (void)regtype;
+ (void)replyDomain;
+ BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)context;
+ NetBrowserInfo* browser = BrowserForSDRef(plugin, sdRef);
+
+ if (!browser) // Missing browser?
+ return;
+
+ if (errorCode != kDNSServiceErr_NoError)
+ return;
+
+ CFStringRef cfServiceName = CFStringCreateWithCString(NULL, serviceName, kCFStringEncodingUTF8);
+
+ if (flags & kDNSServiceFlagsAdd)
+ {
+ HandleTemporaryEventsForService(plugin, browser, cfServiceName, plugin->_onAddEvents);
+ HandleStateEventsForService(plugin, browser, cfServiceName, true);
+ }
+ else
+ {
+ HandleTemporaryEventsForService(plugin, browser, cfServiceName, plugin->_onRemoveEvents);
+ HandleStateEventsForService(plugin, browser, cfServiceName, false);
+ }
+
+ CFRelease(cfServiceName);
+}
+
+/*****************************************************************************
+ * HandleTemporaryEventsForService
+ * -
+ * This method handles the firing of one shot events. Aka. Events that are
+ * signaled when a service appears / disappears. They have a temporarly
+ * signaled state.
+ *****************************************************************************/
+void HandleTemporaryEventsForService(BonjourUserEventsPlugin* plugin, NetBrowserInfo* browser, CFStringRef serviceName, CFMutableDictionaryRef eventsDictionary)
+{
+ CFArrayRef events = (CFArrayRef)CFDictionaryGetValue(eventsDictionary, browser); // Get events for the browser we passed in.
+ CFIndex i;
+ CFIndex count;
+
+ if (!events) // Somehow we have a orphan browser...
+ return;
+
+ count = CFArrayGetCount(events);
+
+ // Go thru the events and run filters, notifity if they pass.
+ for (i = 0; i < count; ++i)
+ {
+ CFDictionaryRef eventDict = (CFDictionaryRef)CFArrayGetValueAtIndex(events, i);
+ CFStringRef eventServiceName = (CFStringRef)CFDictionaryGetValue(eventDict, sServiceNameKey);
+ CFNumberRef token = (CFNumberRef) CFDictionaryGetValue(eventDict, sLaunchdTokenKey);
+
+ // Currently we only filter on service name, that makes this as simple as...
+ if (!eventServiceName || CFEqual(serviceName, eventServiceName))
+ {
+ // Create Context Info
+ CFRunLoopTimerContext context;
+ TimerContextInfo* info = TimerContextInfoCreate(plugin, token);
+
+ context.version = 0;
+ context.info = info;
+ context.retain = TimerContextInfoRetain;
+ context.release = TimerContextInfoRelease;
+ context.copyDescription = TimerContextInfoCopyDescription;
+
+ // Create and add one shot timer to flip the event off after a second
+ CFRunLoopTimerRef timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + 1.0, 0, 0, 0, TemporaryEventTimerCallout, &context);
+ CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes);
+
+ // Signal Event
+ UserEventAgentSetLaunchEventState(plugin->_pluginContext, token, true);
+
+ // Clean Up
+ TimerContextInfoRelease(info);
+ CFRelease(timer);
+
+ }
+ }
+
+}
+
+/*****************************************************************************
+ * HandleStateEventsForService
+ * -
+ * This method handles the toggling the state of a while exists event to
+ * reflect the network.
+ *****************************************************************************/
+void HandleStateEventsForService(BonjourUserEventsPlugin* plugin, NetBrowserInfo* browser, CFStringRef serviceName, Boolean didAppear)
+{
+ CFArrayRef events = (CFArrayRef)CFDictionaryGetValue(plugin->_whileServiceExist, browser); // Get the _whileServiceExist events that are interested in this browser.
+ CFIndex i;
+ CFIndex count;
+
+ if (!events) // Somehow we have a orphan browser...
+ return;
+
+ count = CFArrayGetCount(events);
+
+ // Go thru the events and run filters, notifity if they pass.
+ for (i = 0; i < count; ++i)
+ {
+ CFDictionaryRef eventDict = (CFDictionaryRef)CFArrayGetValueAtIndex(events, i);
+ CFStringRef eventServiceName = (CFStringRef)CFDictionaryGetValue(eventDict, sServiceNameKey);
+ CFNumberRef token = (CFNumberRef) CFDictionaryGetValue(eventDict, sLaunchdTokenKey);
+
+ // Currently we only filter on service name, that makes this as simple as...
+ if (!eventServiceName || CFEqual(serviceName, eventServiceName))
+ UserEventAgentSetLaunchEventState(plugin->_pluginContext, token, didAppear);
+ }
+}
+
+/*****************************************************************************
+ * TemporaryEventTimerCallout
+ * -
+ * This method is invoked a second after a watched service appears / disappears
+ * to toggle the state of the launch event back to false.
+ *****************************************************************************/
+void TemporaryEventTimerCallout ( CFRunLoopTimerRef timer, void *info )
+{
+ TimerContextInfo* contextInfo = (TimerContextInfo*)info;
+
+ UserEventAgentSetLaunchEventState(contextInfo->plugin->_pluginContext, contextInfo->token, false);
+
+ // Remove from pending timers array.
+ CFIndex i;
+ CFIndex count = CFArrayGetCount(contextInfo->plugin->_timers);
+
+ for (i = 0; i < count; ++i)
+ {
+ CFRunLoopTimerRef item = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(contextInfo->plugin->_timers, i);
+
+ if (item == timer)
+ break;
+ }
+
+ if (i != count)
+ CFArrayRemoveValueAtIndex(contextInfo->plugin->_timers, i);
+}
+
+#pragma mark -
+#pragma mark Convenence
+#pragma mark -
+
+/*****************************************************************************
+ * CStringFromCFString
+ * -
+ * Silly convenence function for dealing with non-critical CFSTR -> cStr
+ * conversions.
+ *****************************************************************************/
+
+const char* CStringFromCFString(CFStringRef string)
+{
+ const char* defaultString = "??????";
+ const char* cstring;
+
+ if (!string)
+ return defaultString;
+
+ cstring = CFStringGetCStringPtr(string, kCFStringEncodingUTF8);
+
+ return (cstring) ? cstring : defaultString;
+
+}
+
+#pragma mark -
+#pragma mark TimerContextInfo "Object"
+#pragma mark -
+
+/*****************************************************************************
+ * TimerContextInfoCreate
+ * -
+ * Convenence for creating TimerContextInfo pseudo-objects
+ *****************************************************************************/
+TimerContextInfo* TimerContextInfoCreate(BonjourUserEventsPlugin* plugin, CFNumberRef token)
+{
+ TimerContextInfo* info = malloc(sizeof(TimerContextInfo));
+
+ info->refCount = 1;
+ info->plugin = plugin;
+ info->token = (CFNumberRef)CFRetain(token);
+
+ return info;
+}
+
+/*****************************************************************************
+ * TimerContextInfoRetain
+ * -
+ * Convenence for retaining TimerContextInfo pseudo-objects
+ *****************************************************************************/
+const void* TimerContextInfoRetain(const void* info)
+{
+ TimerContextInfo* context = (TimerContextInfo*)info;
+
+ if (!context)
+ return NULL;
+
+ ++context->refCount;
+
+ return context;
+}
+
+/*****************************************************************************
+ * TimerContextInfoRelease
+ * -
+ * Convenence for releasing TimerContextInfo pseudo-objects
+ *****************************************************************************/
+void TimerContextInfoRelease(const void* info)
+{
+ TimerContextInfo* context = (TimerContextInfo*)info;
+
+ if (!context)
+ return;
+
+ if (context->refCount == 1)
+ {
+ CFRelease(context->token);
+ free(context);
+ return;
+ }
+ else
+ {
+ --context->refCount;
+ }
+}
+
+/*****************************************************************************
+ * TimerContextInfoCopyDescription
+ * -
+ * This method actually does nothing, but is just a stub so CF is happy.
+ *****************************************************************************/
+CFStringRef TimerContextInfoCopyDescription(const void* info)
+{
+ (void)info;
+ return CFStringCreateWithCString(NULL, "TimerContextInfo: No useful description", kCFStringEncodingUTF8);
+}
+
+
+#pragma mark -
+#pragma mark NetBrowserInfo "Object"
+#pragma mark -
+/*****************************************************************************
+ * NetBrowserInfoCreate
+ * -
+ * The method creates a NetBrowserInfo Object and initalizes it.
+ *****************************************************************************/
+NetBrowserInfo* NetBrowserInfoCreate(CFStringRef serviceType, CFStringRef domain, void* context)
+{
+ NetBrowserInfo* outObj = NULL;
+ DNSServiceRef browserRef = NULL;
+ char* cServiceType = NULL;
+ char* cDomain = NULL;
+ Boolean success = true;
+
+ CFIndex serviceSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(serviceType), kCFStringEncodingUTF8);
+ cServiceType = calloc(serviceSize, 1);
+ success = CFStringGetCString(serviceType, cServiceType, serviceSize, kCFStringEncodingUTF8);
+
+ if (domain)
+ {
+ CFIndex domainSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(domain), kCFStringEncodingUTF8);
+ cDomain = calloc(serviceSize, 1);
+ success = success && CFStringGetCString(domain, cDomain, domainSize, kCFStringEncodingUTF8);
+ }
+
+ if (!success)
+ {
+ fprintf(stderr, "LaunchEvent has badly encoded service type or domain.\n");
+ free(cServiceType);
+
+ if (cDomain)
+ free(cDomain);
+
+ return NULL;
+ }
+
+ DNSServiceErrorType err = DNSServiceBrowse(&browserRef, 0, 0, cServiceType, cDomain, ServiceBrowserCallback, context);
+
+ if (err != kDNSServiceErr_NoError)
+ {
+ fprintf(stderr, "Failed to create browser for %s, %s\n", cServiceType, cDomain);
+ free(cServiceType);
+
+ if (cDomain)
+ free(cDomain);
+
+ return NULL;
+ }
+
+ DNSServiceSetDispatchQueue(browserRef, dispatch_get_main_queue());
+
+
+ outObj = malloc(sizeof(NetBrowserInfo));
+
+ outObj->refCount = 1;
+ outObj->browserRef = browserRef;
+
+ free(cServiceType);
+
+ if (cDomain)
+ free(cDomain);
+
+ return outObj;
+}
+
+/*****************************************************************************
+ * NetBrowserInfoRetain
+ * -
+ * The method retains a NetBrowserInfo object.
+ *****************************************************************************/
+const void* NetBrowserInfoRetain(CFAllocatorRef allocator, const void* info)
+{
+ (void)allocator;
+ NetBrowserInfo* obj = (NetBrowserInfo*)info;
+
+ if (!obj)
+ return NULL;
+
+ ++obj->refCount;
+
+ return obj;
+}
+
+/*****************************************************************************
+ * NetBrowserInfoRelease
+ * -
+ * The method releases a NetBrowserInfo object.
+ *****************************************************************************/
+void NetBrowserInfoRelease(CFAllocatorRef allocator, const void* info)
+{
+ (void)allocator;
+ NetBrowserInfo* obj = (NetBrowserInfo*)info;
+
+ if (!obj)
+ return;
+
+ if (obj->refCount == 1)
+ {
+ DNSServiceRefDeallocate(obj->browserRef);
+ free(obj);
+ }
+ else
+ {
+ --obj->refCount;
+ }
+
+}
+
+/*****************************************************************************
+ * NetBrowserInfoEqual
+ * -
+ * The method is used to compare two NetBrowserInfo objects for equality.
+ *****************************************************************************/
+Boolean NetBrowserInfoEqual(const void *value1, const void *value2)
+{
+ NetBrowserInfo* obj1 = (NetBrowserInfo*)value1;
+ NetBrowserInfo* obj2 = (NetBrowserInfo*)value2;
+
+ if (obj1->browserRef == obj2->browserRef)
+ return true;
+
+ return false;
+}
+
+/*****************************************************************************
+ * NetBrowserInfoHash
+ * -
+ * The method is used to make a hash for the object. We can cheat and use the
+ * browser pointer.
+ *****************************************************************************/
+CFHashCode NetBrowserInfoHash(const void *value)
+{
+ return (CFHashCode)((NetBrowserInfo*)value)->browserRef;
+}
+
+
+/*****************************************************************************
+ * NetBrowserInfoCopyDescription
+ * -
+ * Make CF happy.
+ *****************************************************************************/
+CFStringRef NetBrowserInfoCopyDescription(const void *value)
+{
+ (void)value;
+ return CFStringCreateWithCString(NULL, "NetBrowserInfo: No useful description", kCFStringEncodingUTF8);
+}
\ No newline at end of file
--- /dev/null
+/*
+ *
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <net/if.h>
+#include <System/net/pfvar.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <AssertMacros.h>
+#include "P2PPacketFilter.h"
+
+#define AIRDROP_ANCHOR_PATH "com.apple/200.AirDrop"
+#define MDNS_ANCHOR_NAME "Bonjour"
+#define MDNS_ANCHOR_PATH AIRDROP_ANCHOR_PATH "/" MDNS_ANCHOR_NAME
+
+#define PF_DEV_PATH "/dev/pf"
+#define BONJOUR_PORT 5353
+
+static int openPFDevice( int * outFD )
+{
+ int err;
+ int fd = open( PF_DEV_PATH, O_RDWR );
+
+ if( fd >= 0 )
+ {
+ err = 0;
+ *outFD = fd;
+ }
+ else
+ {
+ err = errno;
+ }
+
+ return err;
+}
+
+static int getTicket( int devFD, u_int32_t * outTicket, char * anchorPath )
+{
+ struct pfioc_trans_e trans_e;
+
+ trans_e.rs_num = PF_RULESET_FILTER;
+ strlcpy( trans_e.anchor, anchorPath, sizeof( trans_e.anchor ) );
+
+ struct pfioc_trans trans;
+
+ trans.size = 1;
+ trans.esize = sizeof( trans_e );
+ trans.array = &trans_e;
+
+ int result, ioctlError;
+
+ ioctlError = ioctl( devFD, DIOCXBEGIN, &trans );
+ if( ioctlError )
+ {
+ result = errno;
+ }
+ else
+ {
+ result = 0;
+ *outTicket = trans_e.ticket;
+ }
+
+ return result;
+}
+
+static int commitChange( int devFD, u_int32_t ticket, char * anchorPath )
+{
+ struct pfioc_trans_e trans_e;
+
+ trans_e.rs_num = PF_RULESET_FILTER;
+ strlcpy( trans_e.anchor, anchorPath, sizeof( trans_e.anchor ) );
+ trans_e.ticket = ticket;
+
+ struct pfioc_trans trans;
+
+ trans.size = 1;
+ trans.esize = sizeof( trans_e );
+ trans.array = &trans_e;
+
+ int result, ioctlError;
+
+ ioctlError = ioctl( devFD, DIOCXCOMMIT, &trans );
+ if( ioctlError )
+ result = errno;
+ else
+ result = 0;
+
+ return result;
+}
+
+static int getPoolTicket( int devFD, u_int32_t * outPoolTicket )
+{
+ struct pfioc_pooladdr pp;
+
+ int result, ioctlError;
+
+ ioctlError = ioctl( devFD, DIOCBEGINADDRS, &pp );
+ if( ioctlError )
+ {
+ result = errno;
+ }
+ else
+ {
+ result = 0;
+ *outPoolTicket = pp.ticket;
+ }
+
+ return result;
+}
+
+static int addRule( int devFD, struct pfioc_rule * pr )
+{
+ int result, ioctlResult;
+
+ ioctlResult = ioctl( devFD, DIOCADDRULE, pr );
+ if( ioctlResult )
+ result = errno;
+ else
+ result = 0;
+
+ return result;
+}
+
+static void initRuleHeader( struct pfioc_rule * pr,
+ u_int32_t ticket,
+ u_int32_t poolTicket,
+ char * anchorPath )
+{
+ pr->action = PF_CHANGE_NONE;
+ pr->ticket = ticket;
+ pr->pool_ticket = poolTicket;
+ strlcpy( pr->anchor, anchorPath, sizeof( pr->anchor ) );
+}
+
+// allow inbound traffice on the Bonjour port (5353)
+static void initBonjourRule( struct pfioc_rule * pr,
+ const char * interfaceName,
+ u_int32_t ticket,
+ u_int32_t poolTicket,
+ char * anchorPath )
+{
+ memset( pr, 0, sizeof( *pr ) );
+
+ // Header
+ initRuleHeader( pr, ticket, poolTicket, anchorPath );
+
+ // Rule
+ pr->rule.dst.xport.range.port[0] = htons( BONJOUR_PORT );
+ pr->rule.dst.xport.range.op = PF_OP_EQ;
+
+ strlcpy( pr->rule.ifname, interfaceName, sizeof( pr->rule.ifname ) );
+
+ pr->rule.action = PF_PASS;
+ pr->rule.direction = PF_IN;
+ pr->rule.keep_state = 1;
+ pr->rule.af = AF_INET6;
+ pr->rule.proto = IPPROTO_UDP;
+ pr->rule.extfilter = PF_EXTFILTER_APD;
+}
+
+// allow outbound TCP connections and return traffic for those connections
+static void initOutboundTCPRule( struct pfioc_rule * pr,
+ const char * interfaceName,
+ u_int32_t ticket,
+ u_int32_t poolTicket,
+ char * anchorPath )
+{
+ memset( pr, 0, sizeof( *pr ) );
+
+ // Header
+ initRuleHeader( pr, ticket, poolTicket, anchorPath );
+
+ // Rule
+ strlcpy( pr->rule.ifname, interfaceName, sizeof( pr->rule.ifname ) );
+
+ pr->rule.action = PF_PASS;
+ pr->rule.direction = PF_OUT;
+ pr->rule.keep_state = 1;
+ pr->rule.proto = IPPROTO_TCP;
+}
+
+// allow inbound traffic on the specified port and protocol
+static void initPortRule( struct pfioc_rule * pr,
+ const char * interfaceName,
+ u_int32_t ticket,
+ u_int32_t poolTicket,
+ char * anchorPath,
+ u_int16_t port,
+ u_int16_t protocol )
+{
+ memset( pr, 0, sizeof( *pr ) );
+
+ // Header
+ initRuleHeader( pr, ticket, poolTicket, anchorPath );
+
+ // Rule
+ // mDNSResponder passes the port in Network Byte Order, so htons(port) is not required
+ pr->rule.dst.xport.range.port[0] = port;
+ pr->rule.dst.xport.range.op = PF_OP_EQ;
+
+ strlcpy( pr->rule.ifname, interfaceName, sizeof( pr->rule.ifname ) );
+
+ pr->rule.action = PF_PASS;
+ pr->rule.direction = PF_IN;
+ pr->rule.keep_state = 1;
+ pr->rule.af = AF_INET6;
+ pr->rule.proto = protocol;
+ pr->rule.extfilter = PF_EXTFILTER_APD;
+}
+
+// allow inbound traffice on the Bonjour port (5353) and the specified port and protocol
+int P2PPacketFilterAddBonjourRuleSet(const char * interfaceName, u_int16_t port, u_int16_t protocol )
+{
+ int result;
+ u_int32_t ticket, poolTicket;
+ int devFD = -1;
+ char * anchorPath = MDNS_ANCHOR_PATH;
+
+ result = openPFDevice( &devFD );
+ require( result == 0, exit );
+
+ result = getTicket( devFD, &ticket, anchorPath );
+ require( result == 0, exit );
+
+ result = getPoolTicket( devFD, &poolTicket );
+ require( result == 0, exit );
+
+ struct pfioc_rule pr;
+
+ // allow inbound Bonjour traffice to port 5353
+ initBonjourRule( &pr, interfaceName, ticket, poolTicket, anchorPath);
+
+ result = addRule( devFD, &pr );
+ require( result == 0, exit );
+
+ // open inbound port for service
+ initPortRule( &pr, interfaceName, ticket, poolTicket, anchorPath, port, protocol );
+
+ result = addRule( devFD, &pr );
+ require( result == 0, exit );
+
+ // allow outbound TCP connections and return traffic for those connections
+ initOutboundTCPRule( &pr, interfaceName, ticket, poolTicket, anchorPath);
+
+ result = addRule( devFD, &pr );
+ require( result == 0, exit );
+
+ result = commitChange( devFD, ticket, anchorPath );
+ require( result == 0, exit );
+
+exit:
+
+ if( devFD >= 0 )
+ close( devFD );
+
+ return result;
+}
+
+int P2PPacketFilterClearBonjourRules()
+{
+ int result;
+ int pfDev = -1;
+ u_int32_t ticket;
+ char * anchorPath = MDNS_ANCHOR_PATH;
+
+ result = openPFDevice( &pfDev );
+ require( result == 0, exit );
+
+ result = getTicket( pfDev, &ticket, anchorPath );
+ require( result == 0, exit );
+
+ result = commitChange( pfDev, ticket, anchorPath );
+
+exit:
+
+ if( pfDev >= 0 )
+ close( pfDev );
+
+ return result;
+}
+
--- /dev/null
+/*
+ *
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _P2P_PACKET_FILTER_H_
+#define _P2P_PACKET_FILTER_H_
+
+enum {
+ PF_SET_RULES,
+ PF_CLEAR_RULES
+};
+
+int P2PPacketFilterAddBonjourRuleSet(const char * interfaceName, u_int16_t port, u_int16_t protocol );
+int P2PPacketFilterClearBonjourRules(void);
+
+#endif /* _P2P_PACKET_FILTER_H_ */
static const char kmDNSBootstrapName[] = "com.apple.mDNSResponderRestart";
static mach_port_t m_port = MACH_PORT_NULL;
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
mDNSlocal void PrepareForIdle(void *m_param);
-#else __LIB_DISPATCH__
+#else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
static mach_port_t client_death_port = MACH_PORT_NULL;
static mach_port_t signal_port = MACH_PORT_NULL;
-#endif __LIB_DISPATCH__
+#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
static mach_port_t server_priv_port = MACH_PORT_NULL;
static mDNSBool advertise = mDNS_Init_AdvertiseLocalAddresses; // By default, advertise addresses (& other records) via multicast
extern mDNSBool StrictUnicastOrdering;
+extern mDNSBool AlwaysAppendSearchDomains;
//*************************************************************************************************************
#if COMPILER_LIKES_PRAGMA_MARK
return(e || b || l || r);
}
-#ifndef __LIB_DISPATCH__
+#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
mDNSlocal void ClientDeathCallback(CFMachPortRef unusedport, void *voidmsg, CFIndex size, void *info)
{
KQueueUnlock(&mDNSStorage, "Mach AbortClient");
}
-#endif __LIB_DISPATCH__
+#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
mDNSlocal void EnableDeathNotificationForClient(mach_port_t ClientMachPort, void *m)
{
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
dispatch_source_t mach_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND, ClientMachPort, 0, dispatch_get_main_queue());
if (mach_source == mDNSNULL)
{
mach_port_destroy(mach_task_self(), ClientMachPort);
});
dispatch_resume(mach_source);
-#else __LIB_DISPATCH__
+#else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
mach_port_t prev;
kern_return_t r = mach_port_request_notification(mach_task_self(), ClientMachPort, MACH_NOTIFY_DEAD_NAME, 0,
client_death_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev);
// If the port already died while we were thinking about it, then abort the operation right away
if (r != KERN_SUCCESS)
AbortClientWithLogMessage(ClientMachPort, "died/deallocated before we could enable death notification", "", m);
-#endif __LIB_DISPATCH__
+#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
}
//*************************************************************************************************************
si->domain = *domain;
err = mDNS_RegisterService(&mDNSStorage, &si->srs, &si->name, &x->type, domain, NULL,
- x->port, x->txtinfo, x->txt_len, SubTypes, x->NumSubTypes, mDNSInterface_Any, RegCallback, si);
+ x->port, x->txtinfo, x->txt_len, SubTypes, x->NumSubTypes, mDNSInterface_Any, RegCallback, si, 0);
if (!err)
{
si->next = x->regs;
// Do the operation
LogOperation("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) REF %p",
client, si->srs.RR_SRV.resrec.name->c, type, data_len, extra);
- err = mDNS_AddRecordToService(&mDNSStorage, &si->srs, extra, &extra->r.rdatastorage, ttl);
+ err = mDNS_AddRecordToService(&mDNSStorage, &si->srs, extra, &extra->r.rdatastorage, ttl, 0);
if (err)
{
mDNS_StartExit(&mDNSStorage);
}
-#ifndef __LIB_DISPATCH__
+#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
// Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit
mDNSlocal void HandleSIG(int sig)
{
- // WARNING: can't call syslog or fprintf from signal handler
+ kern_return_t status;
mach_msg_header_t header;
+
+ // WARNING: can't call syslog or fprintf from signal handler
header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
header.msgh_remote_port = signal_port;
header.msgh_local_port = MACH_PORT_NULL;
header.msgh_size = sizeof(header);
header.msgh_id = sig;
- if (mach_msg_send(&header) != MACH_MSG_SUCCESS)
- if (sig == SIGTERM || sig == SIGINT) exit(-1);
- }
-mDNSlocal void CatchABRT(int sig)
- {
- // WARNING: can't call syslog or fprintf from signal handler
- // We want a CrashReporter stack trace so we can find out what library called abort()
- // So that we will crash, unblock all signals (that abort() may have blocked)
- sigset_t mask;
- sigfillset(&mask);
- sigprocmask(SIG_UNBLOCK, &mask, NULL);
- (void)sig;
- while(1) *(long*)0 = 0;
+ status = mach_msg(&header, MACH_SEND_MSG | MACH_SEND_TIMEOUT, header.msgh_size,
+ 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
+
+ if (status != MACH_MSG_SUCCESS)
+ {
+ if (status == MACH_SEND_TIMED_OUT) mach_msg_destroy(&header);
+ if (sig == SIGTERM || sig == SIGINT) exit(-1);
+ }
}
-#endif __LIB_DISPATCH__
+#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
mDNSlocal void INFOCallback(void)
{
DNSServiceRegistration *r;
NetworkInterfaceInfoOSX *i;
DNSServer *s;
+ McastResolver *mr;
- LogMsg("---- BEGIN STATE LOG ---- %s", mDNSResponderVersionString);
+ LogMsg("---- BEGIN STATE LOG ---- %s %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers);
udsserver_info(&mDNSStorage);
{
// Allow six characters for interface name, for names like "vmnet8"
if (!i->Exists)
- LogMsgNoIdent("%p (%p), Registered %p, %s %-6s(%lu) %.6a %.6a %#-14a dormant for %d seconds",
- i->ifinfo.InterfaceID, i, i->Registered,
+ LogMsgNoIdent("%p %2ld, Registered %p, %s %-6s(%lu) %.6a %.6a %#-14a dormant for %d seconds",
+ i, i->ifinfo.InterfaceID, i->Registered,
i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifinfo.ifname, i->scope_id, &i->ifinfo.MAC, &i->BSSID,
&i->ifinfo.ip, utc - i->LastSeen);
else
{
const CacheRecord *sps[3];
FindSPSInCache(&mDNSStorage, &i->ifinfo.NetWakeBrowse, sps);
- LogMsgNoIdent("%p (%p), Registered %p, %s %-6s(%lu) %.6a %.6a %s %s %-15.4a %s %s %s %s %#a",
- i->ifinfo.InterfaceID, i, i->Registered,
+ LogMsgNoIdent("%p %2ld, Registered %p, %s %-6s(%lu) %.6a %.6a %s %s %-15.4a %s %s %s %s %#a",
+ i, i->ifinfo.InterfaceID, i->Registered,
i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifinfo.ifname, i->scope_id, &i->ifinfo.MAC, &i->BSSID,
i->ifinfo.InterfaceActive ? "Active" : " ",
i->ifinfo.IPv4Available ? "v4" : " ",
for (s = mDNSStorage.DNSServers; s; s = s->next)
{
NetworkInterfaceInfoOSX *ifx = IfindexToInterfaceInfoOSX(&mDNSStorage, s->interface);
- LogMsgNoIdent("DNS Server %##s %s%s%#a:%d %d %s %s",
+ LogMsgNoIdent("DNS Server %##s %s%s%#a:%d %d %s %d %s",
s->domain.c, ifx ? ifx->ifinfo.ifname : "", ifx ? " " : "", &s->addr, mDNSVal16(s->port),
s->penaltyTime ? s->penaltyTime - mDNS_TimeNow(&mDNSStorage) : 0, s->scoped ? "Scoped" : "",
+ s->timeout,
s->teststate == DNSServer_Untested ? "(Untested)" :
s->teststate == DNSServer_Passed ? "" :
s->teststate == DNSServer_Failed ? "(Failed)" :
}
}
+ LogMsgNoIdent("--------- Mcast Resolvers ----------");
+ if (!mDNSStorage.McastResolvers) LogMsgNoIdent("<None>");
+ else
+ {
+ for (mr = mDNSStorage.McastResolvers; mr; mr = mr->next)
+ LogMsgNoIdent("Mcast Resolver %##s timeout %u", mr->domain.c, mr->timeout);
+ }
+
mDNSs32 now = mDNS_TimeNow(&mDNSStorage);
LogMsgNoIdent("Timenow 0x%08lX (%d)", (mDNSu32)now, now);
- LogMsg("---- END STATE LOG ---- %s", mDNSResponderVersionString);
+ LogMsg("---- END STATE LOG ---- %s %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers);
}
-#ifndef __LIB_DISPATCH__
+#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
{
{
s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL);
m_port = CFMachPortGetPort(s_port);
- char *MachServerName = OSXVers < OSXVers_10_3_Panther ? "DNSServiceDiscoveryServer" : "com.apple.mDNSResponder";
- kern_return_t status = bootstrap_register(bootstrap_port, MachServerName, m_port);
+ kern_return_t status = bootstrap_register(bootstrap_port, "com.apple.mDNSResponder", m_port);
if (status)
{
return(err);
}
-#else __LIB_DISPATCH__
+#else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
// SignalDispatch is mostly just a copy/paste of entire code block from SignalCallback above.
// The common code should be a subroutine, or we end up having to fix bugs in two places all the time.
// The same applies to mDNSDaemonInitialize, much of which is just a copy/paste of chunks
// of code from above. Alternatively we could remove the duplicated source code by having
-// single routines, with the few differing parts bracketed with "#ifndef __LIB_DISPATCH__"
+// single routines, with the few differing parts bracketed with "#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM"
mDNSlocal void SignalDispatch(dispatch_source_t source)
{
{
s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL);
m_port = CFMachPortGetPort(s_port);
- char *MachServerName = OSXVers < OSXVers_10_3_Panther ? "DNSServiceDiscoveryServer" : "com.apple.mDNSResponder";
- kern_return_t status = bootstrap_register(bootstrap_port, MachServerName, m_port);
+ kern_return_t status = bootstrap_register(bootstrap_port, "com.apple.mDNSResponder", m_port);
if (status)
{
return(err);
}
-#endif __LIB_DISPATCH__
+#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
mDNSlocal mDNSs32 mDNSDaemonIdle(mDNS *const m)
{
mDNSs32 now = mDNS_TimeNow(m);
- // 1. If we have network change events to handle, do them FIRST, before calling mDNS_Execute()
+ // 1. If we need to set domain secrets, do so before handling the network change
+ // Detailed reason:
+ // BTMM domains listed in DynStore Setup:/Network/BackToMyMac are added to the registration domains list,
+ // and we need to setup the associated AutoTunnel DomainAuthInfo entries before that happens.
+ if (m->p->KeyChainTimer && now - m->p->KeyChainTimer >= 0)
+ {
+ m->p->KeyChainTimer = 0;
+ mDNS_Lock(m);
+ SetDomainSecrets(m);
+ mDNS_Unlock(m);
+ }
+
+ // 2. If we have network change events to handle, do them before calling mDNS_Execute()
// Detailed reason:
// mDNSMacOSXNetworkChanged() currently closes and re-opens its sockets. If there are received packets waiting, they are lost.
// mDNS_Execute() generates packets, including multicasts that are looped back to ourself.
if (m->p->RequestReSleep && now - m->p->RequestReSleep >= 0) { m->p->RequestReSleep = 0; mDNSPowerRequest(0, 0); }
- // KeyChain frequently fails to notify clients of change events. To work around this
- // we set a timer and periodically poll to detect if any changes have occurred.
- // Without this Back To My Mac just does't work for a large number of users.
- // See <rdar://problem/5124399> Not getting Keychain Changed events when enabling BTMM
- if (m->p->KeyChainBugTimer && now - m->p->KeyChainBugTimer >= 0)
- {
- m->p->KeyChainBugInterval *= 2;
- m->p->KeyChainBugTimer = NonZeroTime(now + m->p->KeyChainBugInterval);
- if (m->p->KeyChainBugInterval > 2 * mDNSPlatformOneSecond) m->p->KeyChainBugTimer = 0;
- mDNS_Lock(m);
- SetDomainSecrets(m);
- mDNS_Unlock(m);
- }
-
- // 2. Call mDNS_Execute() to let mDNSCore do what it needs to do
+ // 3. Call mDNS_Execute() to let mDNSCore do what it needs to do
mDNSs32 nextevent = mDNS_Execute(m);
if (m->p->NetworkChanged)
if (nextevent - m->p->NetworkChanged > 0)
nextevent = m->p->NetworkChanged;
- if (m->p->KeyChainBugTimer)
- if (nextevent - m->p->KeyChainBugTimer > 0)
- nextevent = m->p->KeyChainBugTimer;
+ if (m->p->KeyChainTimer)
+ if (nextevent - m->p->KeyChainTimer > 0)
+ nextevent = m->p->KeyChainTimer;
if (m->p->RequestReSleep)
if (nextevent - m->p->RequestReSleep > 0)
nextevent = m->p->RequestReSleep;
- // 3. Deliver any waiting browse messages to clients
+ // 4. Deliver any waiting browse messages to clients
DNSServiceBrowser *b = DNSServiceBrowserList;
while (b)
if (result == kIOReturnNotReady)
{
- LogMsg("Requested wakeup in %d seconds unsuccessful; retrying with longer intervals", interval);
+ int r;
+ LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful; retrying with longer intervals", interval);
// IOPMSchedulePowerEvent fails with kIOReturnNotReady (-536870184/0xe00002d8) if the
// requested wake time is "too soon", but there's no API to find out what constitutes
// "too soon" on any given OS/hardware combination, so if we get kIOReturnNotReady
// we just have to iterate with successively longer intervals until it doesn't fail.
- // Additionally, if our power request is deemed "too soon" for the machine to get to
- // sleep and wake back up again, we attempt to cancel the sleep request, since the
- // implication is that the system won't manage to be awake again at the time we need it.
+ // We preserve the value of "result" because if our original power request was deemed "too soon"
+ // for the machine to get to sleep and wake back up again, we attempt to cancel the sleep request,
+ // since the implication is that the system won't manage to be awake again at the time we need it.
do
{
interval += (interval < 20) ? 1 : ((interval+3) / 4);
- result = mDNSPowerRequest(1, interval);
+ r = mDNSPowerRequest(1, interval);
}
- while (result == kIOReturnNotReady);
+ while (r == kIOReturnNotReady);
+ if (r) LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful: %d %X", interval, r, r);
+ else LogSPS("AllowSleepNow: Requested later wakeup in %d seconds; will also attempt IOCancelPowerChange", interval);
+ }
+ else
+ {
+ if (result) LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful: %d %X", interval, result, result);
+ else LogSPS("AllowSleepNow: Requested wakeup in %d seconds", interval);
}
-
- if (result) LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful: %d %X", interval, result, result);
- else LogSPS("AllowSleepNow: Requested wakeup in %d seconds", interval);
m->p->WakeAtUTC = mDNSPlatformUTC() + interval;
}
}
- // Clear our interface list to empty state, ready to go to sleep
- // As a side effect of doing this, we'll also cancel any outstanding SPS Resolve calls that didn't complete
m->SleepState = SleepState_Sleeping;
- mDNSMacOSXNetworkChanged(m);
+ // We used to clear our interface list to empty state here before going to sleep.
+ // The applications that try to connect to an external server during maintenance wakes, saw
+ // DNS resolution errors as we don't have any interfaces (most queries use SuppressUnusable
+ // flag). Thus, we don't remove our interfaces anymore on sleep.
}
LogSPS("AllowSleepNow: %s(%lX) %s at %ld (%d ticks remaining)",
return(mDNStrue);
}
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
mDNSexport void TriggerEventCompletion()
{
return;
}
-#else __LIB_DISPATCH__
+#else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
mDNSlocal void KQWokenFlushBytes(int fd, __unused short filter, __unused void *context)
{
return NULL;
}
-#endif __LIB_DISPATCH__
+#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
mDNSlocal void LaunchdCheckin(void)
{
int i;
kern_return_t status;
- LogMsg("%s starting", mDNSResponderVersionString);
+ mDNSMacOSXSystemBuildNumber(NULL);
+ LogMsg("%s starting %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers);
#if 0
LogMsg("CacheRecord %d", sizeof(CacheRecord));
if (!strcasecmp(argv[i], "-DebugLogging" )) mDNS_LoggingEnabled = mDNStrue;
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;
+ OfferSleepProxyService = (i+1 < argc && mDNSIsDigit(argv[i+1][0]) && mDNSIsDigit(argv[i+1][1]) && argv[i+1][2]==0) ? atoi(argv[++i]) : 100;
+ if (!strcasecmp(argv[i], "-UseInternalSleepProxy" ))
+ UseInternalSleepProxy = (i+1<argc && mDNSIsDigit(argv[i+1][0]) && argv[i+1][1]==0) ? atoi(argv[++i]) : 1;
+ if (!strcasecmp(argv[i], "-StrictUnicastOrdering" )) StrictUnicastOrdering = mDNStrue;
if (!strcasecmp(argv[i], "-DisableInboundRelay" )) DisableInboundRelayConnection = mDNStrue;
+ if (!strcasecmp(argv[i], "-AlwaysAppendSearchDomains")) AlwaysAppendSearchDomains = mDNStrue;
}
// Note that mDNSPlatformInit will set DivertMulticastAdvertisements in the mDNS structure
if (!advertise) LogMsg("Administratively prohibiting multicast advertisements");
- OSXVers = mDNSMacOSXSystemBuildNumber(NULL);
-
-#ifndef __LIB_DISPATCH__
+#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
signal(SIGHUP, HandleSIG); // (Debugging) Purge the cache to check for cache handling bugs
signal(SIGINT, HandleSIG); // Ctrl-C: Detach from Mach BootstrapService and exit cleanly
- // On 10.5 and later, the default action for SIGABRT is to generate a crash report, so we only need our CatchABRT handler on 10.4
- if (OSXVers <= OSXVers_10_4_Tiger)
- {
- LogInfo("Adding SIGABRT handler");
- signal(SIGABRT, CatchABRT); // For debugging -- SIGABRT should never happen
- }
signal(SIGPIPE, SIG_IGN ); // Don't want SIGPIPE signals -- we'll handle EPIPE errors directly
signal(SIGTERM, HandleSIG); // Machine shutting down: Detach from and exit cleanly like Ctrl-C
signal(SIGINFO, HandleSIG); // (Debugging) Write state snapshot to syslog
signal(SIGUSR1, HandleSIG); // (Debugging) Enable Logging
signal(SIGUSR2, HandleSIG); // (Debugging) Enable Packet Logging
-#endif __LIB_DISPATCH__
+#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
mDNSStorage.p = &PlatformStorage; // Make sure mDNSStorage.p is set up, because validatelists uses it
LaunchdCheckin();
}
}
-#ifndef __LIB_DISPATCH__
+#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
// Create the kqueue, mutex and thread to support KQSockets
KQueueFD = kqueue();
PlatformStorage.WakeKQueueLoopFD = fdpair[0];
KQueueSet(fdpair[1], EV_ADD, EVFILT_READ, &wakeKQEntry);
-#endif __LIB_DISPATCH__
+#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
// Invoke sandbox profile /usr/share/sandbox/mDNSResponder.sb
#if MDNS_NO_SANDBOX
else
{
char *sandbox_msg;
- struct stat s;
uint64_t sandbox_flags = SANDBOX_NAMED;
+ // On Desktop, the sandbox profile always exists under /usr/share/sandbox, no need to
+ // check it. stat results in calls to opendirectoryd and to avoid deadlocks with
+ // opendirectoryd early on, we avoid this call.
+#if TARGET_OS_EMBEDDED
+ struct stat s;
if (stat("/usr/share/sandbox/mDNSResponder.sb", &s) == 0)
{
sandbox_flags = SANDBOX_NAMED_EXTERNAL;
LogInfo("Will load Sandbox profile from filesystem");
}
+#endif
int sandbox_err = sandbox_init("mDNSResponder", sandbox_flags, &sandbox_msg);
if (sandbox_err) { LogMsg("WARNING: sandbox_init error %s", sandbox_msg); sandbox_free_error(sandbox_msg); }
mDNSMacOSXNetworkChanged(&mDNSStorage);
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
LogInfo("Daemon Start: Using LibDispatch");
// CFRunLoopRun runs both CFRunLoop sources and dispatch sources
CFRunLoopRun();
-#else __LIB_DISPATCH__
+#else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
// Start the kqueue thread
pthread_t KQueueThread;
i = pthread_create(&KQueueThread, NULL, KQueueLoop, &mDNSStorage);
LogMsg("ERROR: CFRunLoopRun Exiting.");
mDNS_Close(&mDNSStorage);
}
-#endif __LIB_DISPATCH__
+#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
LogMsg("%s exiting", mDNSResponderVersionString);
newSource->kqs.KQcallback = callback;
newSource->kqs.KQcontext = context;
newSource->kqs.KQtask = "UDS client";
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
newSource->kqs.readSource = mDNSNULL;
newSource->kqs.writeSource = mDNSNULL;
newSource->kqs.fdClosed = mDNSfalse;
-#endif __LIB_DISPATCH__
+#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
if (KQueueSet(fd, EV_ADD, EVFILT_READ, &newSource->kqs) == 0)
{
(void)err;
}
-void mDNSConfigureServer(int updown, const domainname *const fqdn)
+extern const char dnsprefix[];
+
+void mDNSConfigureServer(int updown, const char *const prefix, const domainname *const fqdn)
{
kern_return_t kr = KERN_SUCCESS;
int retry = 0, err = 0;
- char fqdnStr[MAX_ESCAPED_DOMAIN_NAME] = { 0 };
- if (fqdn && ConvertDomainNameToCString(fqdn, fqdnStr))
+ char fqdnStr[MAX_ESCAPED_DOMAIN_NAME + 10] = { 0 }; // Assume the prefix is no larger than 10 chars
+ if (fqdn)
{
- // remove the trailing dot, as that is not used in the keychain entry racoon will lookup
- mDNSu32 fqdnEnd = mDNSPlatformStrLen(fqdnStr);
- if (fqdnEnd) fqdnStr[fqdnEnd - 1] = 0;
+ mDNSPlatformStrCopy(fqdnStr, prefix);
+ if (ConvertDomainNameToCString(fqdn, fqdnStr + mDNSPlatformStrLen(prefix)) && prefix == dnsprefix)
+ {
+ // remove the trailing dot, as that is not used in the keychain entry racoon will lookup
+ mDNSu32 fqdnEnd = mDNSPlatformStrLen(fqdnStr);
+ if (fqdnEnd) fqdnStr[fqdnEnd - 1] = 0;
+ }
}
MACHRETRYLOOP_BEGIN(kr, retry, err, fin);
kr = proxy_mDNSConfigureServer(getHelperPort(retry), updown, fqdnStr);
int mDNSAutoTunnelSetKeys(int replacedelete, v6addr_t local_inner,
v6addr_t local_outer, short local_port, v6addr_t remote_inner,
- v6addr_t remote_outer, short remote_port, const domainname *const fqdn)
+ v6addr_t remote_outer, short remote_port, const char* const prefix, const domainname *const fqdn)
{
kern_return_t kr = KERN_SUCCESS;
int retry = 0, err = 0;
- char fqdnStr[MAX_ESCAPED_DOMAIN_NAME] = { 0 };
- if (fqdn && ConvertDomainNameToCString(fqdn, fqdnStr))
+ char fqdnStr[MAX_ESCAPED_DOMAIN_NAME + 10] = { 0 }; // Assume the prefix is no larger than 10 chars
+ if (fqdn)
{
- // remove the trailing dot, as that is not used in the keychain entry racoon will lookup
- mDNSu32 fqdnEnd = mDNSPlatformStrLen(fqdnStr);
- if (fqdnEnd) fqdnStr[fqdnEnd - 1] = 0;
+ mDNSPlatformStrCopy(fqdnStr, prefix);
+ if (ConvertDomainNameToCString(fqdn, fqdnStr + mDNSPlatformStrLen(prefix)) && prefix == dnsprefix)
+ {
+ // remove the trailing dot, as that is not used in the keychain entry racoon will lookup
+ mDNSu32 fqdnEnd = mDNSPlatformStrLen(fqdnStr);
+ if (fqdnEnd) fqdnStr[fqdnEnd - 1] = 0;
+ }
}
MACHRETRYLOOP_BEGIN(kr, retry, err, fin);
kr = proxy_mDNSAutoTunnelSetKeys(getHelperPort(retry), replacedelete, local_inner, local_outer, local_port, remote_inner, remote_outer, remote_port, fqdnStr, &err);
fin:
(void) err;
}
+
+void mDNSPacketFilterControl(uint32_t command, char * ifname, uint16_t servicePort, uint16_t protocol)
+ {
+ kern_return_t kr = KERN_SUCCESS;
+ int retry = 0, err = 0;
+ MACHRETRYLOOP_BEGIN(kr, retry, err, fin);
+ kr = proxy_mDNSPacketFilterControl(getHelperPort(retry), command, ifname, servicePort, protocol);
+ MACHRETRYLOOP_END(kr, retry, err, fin);
+fin:
+ (void) err;
+ }
#include "helpermsgServer.h"
#include "helper-server.h"
#include "ipsec_options.h"
+#include "P2PPacketFilter.h"
#ifndef RTF_IFSCOPE
#define RTF_IFSCOPE 0x1000000
// Note: the "\xEF\xBB\xBF" byte sequence in the CFS_Format string is the UTF-8 encoding of the zero-width non-breaking space character.
// By appending this invisible character on the end of literal names, we ensure the these strings cannot inadvertently match any string
// in the localization file -- since we know for sure that none of our strings in the localization file contain the ZWNBS character.
- CFS_OQ = CFStringCreateWithCString(NULL, "“", kCFStringEncodingUTF8);
- CFS_CQ = CFStringCreateWithCString(NULL, "”", kCFStringEncodingUTF8);
- CFS_Format = CFStringCreateWithCString(NULL, "%@%s\xEF\xBB\xBF", kCFStringEncodingUTF8);
+ //
+ // For languages that are written right to left, when we mix English (host names could be in english with brackets etc. and the
+ // rest in Arabic) we need unicode markups for proper formatting. The Unicode sequence 202C (UTF8 E2 80 AC), 200E (UTF8 E2 80 8E) and
+ // 202B (UTF8 E2 80 AB) helps with the formatting. See <rdar://problem/8629082> for more details.
+ CFS_OQ = CFStringCreateWithCString(NULL, "“\xE2\x80\xAB", kCFStringEncodingUTF8);
+ CFS_CQ = CFStringCreateWithCString(NULL, "\xE2\x80\xAC”", kCFStringEncodingUTF8);
+ CFS_Format = CFStringCreateWithCString(NULL, "%@%s\xEF\xBB\xBF\xE2\x80\x8E", kCFStringEncodingUTF8);
CFS_ComputerName = CFStringCreateWithCString(NULL, "The name of your computer ", kCFStringEncodingUTF8);
CFS_ComputerNameMsg = CFStringCreateWithCString(NULL, "To change the name of your computer, "
"open System Preferences and click Sharing, then type the name in the Computer Name field.", kCFStringEncodingUTF8);
enum DNSKeyFormat
{
- formatNotDNSKey, formatDdnsTypeItem, formatDnsPrefixedServiceItem
+ formatNotDNSKey, formatDdnsTypeItem, formatDnsPrefixedServiceItem, formatBtmmPrefixedServiceItem
};
// On Mac OS X on Intel, the four-character string seems to be stored backwards, at least sometimes.
static const char dnsprefix[] = "dns:";
static const char ddns[] = "ddns";
static const char ddnsrev[] = "sndd";
+static const char btmmprefix[] = "btmmdns:";
#ifndef NO_SECURITYFRAMEWORK
static enum DNSKeyFormat
getDNSKeyFormat(SecKeychainItemRef item, SecKeychainAttributeList **attributesp)
{
- static UInt32 tags[3] =
+ static UInt32 tags[4] =
{
- kSecTypeItemAttr, kSecServiceItemAttr, kSecAccountItemAttr
+ kSecTypeItemAttr, kSecServiceItemAttr, kSecAccountItemAttr, kSecLabelItemAttr
};
static SecKeychainAttributeInfo attributeInfo =
{
"malformed result from SecKeychainItemCopyAttributesAndData - skipping");
goto skip;
}
+
debug("entry (\"%.*s\", \"%.*s\", \"%.*s\")",
(int)attributes->attr[0].length, attributes->attr[0].data,
(int)attributes->attr[1].length, attributes->attr[1].data,
0 == strncasecmp(attributes->attr[1].data, dnsprefix,
sizeof(dnsprefix)-1))
format = formatDnsPrefixedServiceItem;
+ else if (attributes->attr[1].length >= sizeof(btmmprefix)-1 &&
+ 0 == strncasecmp(attributes->attr[1].data, btmmprefix, sizeof(btmmprefix)-1))
+ format = formatBtmmPrefixedServiceItem;
else if (attributes->attr[0].length == sizeof(ddns)-1 &&
0 == strncasecmp(attributes->attr[0].data, ddns, sizeof(ddns)-1))
format = formatDdnsTypeItem;
return formatNotDNSKey;
}
+// Insert the attributes as defined by mDNSKeyChainAttributes
static CFPropertyListRef
getKeychainItemInfo(SecKeychainItemRef item,
SecKeychainAttributeList *attributes, enum DNSKeyFormat format)
debug("CFArrayCreateMutable failed");
goto error;
}
+
+ // Insert the Account attribute (kmDNSKcWhere)
switch ((enum DNSKeyFormat)format)
{
case formatDdnsTypeItem:
attributes->attr[1].data, attributes->attr[1].length);
break;
case formatDnsPrefixedServiceItem:
+ case formatBtmmPrefixedServiceItem:
data = CFDataCreate(kCFAllocatorDefault,
- attributes->attr[1].data + sizeof(dnsprefix)-1,
- attributes->attr[1].length - (sizeof(dnsprefix)-1));
+ attributes->attr[1].data, attributes->attr[1].length);
+ break;
default:
assert("unknown DNSKeyFormat value");
break;
}
CFArrayAppendValue(entry, data);
CFRelease(data);
+
+ // Insert the Where attribute (kmDNSKcAccount)
if (NULL == (data = CFDataCreate(kCFAllocatorDefault,
attributes->attr[2].data, attributes->attr[2].length)))
{
}
CFArrayAppendValue(entry, data);
CFRelease(data);
+
+ // Insert the Key attribute (kmDNSKcKey)
if (noErr != (status = SecKeychainItemCopyAttributesAndData(item, NULL,
NULL, NULL, &keylen, &keyp)))
{
}
CFArrayAppendValue(entry, data);
CFRelease(data);
+
+ // Insert the Name attribute (kmDNSKcName)
+ if (NULL == (data = CFDataCreate(kCFAllocatorDefault,
+ attributes->attr[3].data, attributes->attr[3].length)))
+ {
+ debug("CFDataCreate for attr[3] failed");
+ goto error;
+ }
+ CFArrayAppendValue(entry, data);
+ CFRelease(data);
return entry;
error:
" situation identity_only;\n"
" verify_identifier off;\n"
" generate_policy on;\n"
- " shared_secret keychain_by_id \"dns:";
+ " shared_secret keychain_by_id \"";
static const char config2[] =
"\";\n"
" nonce_size 16;\n"
do_mDNSAutoTunnelSetKeys(__unused mach_port_t port, int replacedelete,
v6addr_t loc_inner, v6addr_t loc_outer6, uint16_t loc_port,
v6addr_t rmt_inner, v6addr_t rmt_outer6, uint16_t rmt_port,
- const char *fqdn, int *err, audit_token_t token)
+ const char *id, int *err, audit_token_t token)
{
#ifndef MDNS_NO_IPSEC
static const char config[] =
" situation identity_only;\n"
" verify_identifier off;\n"
" generate_policy on;\n"
- " my_identifier user_fqdn \"dns:%s\";\n"
- " shared_secret keychain \"dns:%s\";\n"
+ " my_identifier user_fqdn \"%s\";\n"
+ " shared_secret keychain \"%s\";\n"
" nonce_size 16;\n"
" lifetime time 15 min;\n"
" initial_contact on;\n"
goto fin;
}
fd = -1;
- fprintf(fp, config, configHeader, (!rmt_port ? ro6 : ro), rmt_port, fqdn, fqdn, ri, li, li, ri);
+ fprintf(fp, config, configHeader, (!rmt_port ? ro6 : ro), rmt_port, id, id, ri, li, li, ri);
fclose(fp);
fp = NULL;
if (0 > rename(tmp_path, path))
unlink(tmp_path);
#else
(void)replacedelete; (void)loc_inner; (void)loc_outer6; (void)loc_port; (void)rmt_inner;
- (void)rmt_outer6; (void)rmt_port; (void)fqdn; (void)token;
+ (void)rmt_outer6; (void)rmt_port; (void)id; (void)token;
*err = kmDNSHelperIPsecDisabled;
#endif /* MDNS_NO_IPSEC */
close(bpf_fd);
return KERN_SUCCESS;
}
+
+// Open the specified port for protocol in the P2P firewall.
+kern_return_t
+do_mDNSPacketFilterControl(__unused mach_port_t port, uint32_t command, const char * ifname, uint16_t servicePort, uint16_t protocol, audit_token_t token)
+ {
+ (void) token; // unused
+ int error;
+ kern_return_t result = KERN_SUCCESS;
+
+ helplog(ASL_LEVEL_INFO, "do_mDNSPacketFilterControl: command %d ifname %s, servicePort 0x%x, protocol %d",
+ command, ifname, servicePort, protocol);
+
+ switch (command)
+ {
+ case PF_SET_RULES:
+ error = P2PPacketFilterAddBonjourRuleSet(ifname, servicePort, protocol);
+ if (error)
+ {
+ helplog(ASL_LEVEL_ERR, "P2PPacketFilterAddBonjourRuleSet failed %s", strerror(error));
+ result = KERN_FAILURE;
+ }
+ break;
+
+ case PF_CLEAR_RULES:
+ error = P2PPacketFilterClearBonjourRules();
+ if (error)
+ {
+ helplog(ASL_LEVEL_ERR, "P2PPacketFilterClearBonjourRules failed %s", strerror(error));
+ result = KERN_FAILURE;
+ }
+ break;
+
+ default:
+ helplog(ASL_LEVEL_ERR, "do_mDNSPacketFilterControl: invalid command %d", command);
+ result = KERN_INVALID_ARGUMENT;
+ break;
+ }
+
+ return result;
+ }
+
kmDNSAutoTunnelSetKeysDelete
};
+// helper parses the system keychain and returns the information to mDNSResponder.
+// It returns four attributes. Attributes are defined after how they show up in
+// keychain access utility (the actual attribute name to retrieve these are different).
+enum mDNSKeyChainAttributes
+ {
+ kmDNSKcWhere, // Where
+ kmDNSKcAccount, // Account
+ kmDNSKcKey, // Key
+ kmDNSKcName // Name
+ };
+
#define ERROR(x, y) x,
enum mDNSHelperErrors
{
extern void mDNSPreferencesSetName(int key, domainlabel *old, domainlabel *new);
extern int mDNSKeychainGetSecrets(CFArrayRef *secrets);
extern void mDNSAutoTunnelInterfaceUpDown(int updown, v6addr_t addr);
-extern void mDNSConfigureServer(int updown, const domainname *const fqdn);
+extern void mDNSConfigureServer(int updown, const char *const prefix, const domainname *const fqdn);
extern int mDNSAutoTunnelSetKeys(int replacedelete, v6addr_t local_inner,
v6addr_t local_outer, short local_port, v6addr_t remote_inner,
- v6addr_t remote_outer, short remote_port, const domainname *const fqdn);
+ v6addr_t remote_outer, short remote_port, const char *const prefix, const domainname *const fqdn);
extern void mDNSSendWakeupPacket(unsigned ifid, char *eth_addr, char *ip_addr, int iteration);
+extern void mDNSPacketFilterControl(uint32_t command, char * ifname, uint16_t servicePort, uint16_t protocol);
#endif /* H_HELPER_H */
simpleroutine mDNSConfigureServer(
port : mach_port_t;
updown : int;
- fqdn : string_t;
+ id : string_t;
ServerAuditToken token : audit_token_t);
routine mDNSAutoTunnelSetKeys( port : mach_port_t;
remote_inner : v6addr_t;
remote_outer : v6addr_t;
remote_port : uint16_t; /* Port expressed as a numeric integer value */
- fqdn : string_t;
+ id : string_t;
out err : int;
ServerAuditToken token : audit_token_t);
ip_addr : string_t;
iteration : int;
ServerAuditToken token : audit_token_t);
+
+simpleroutine mDNSPacketFilterControl(
+ port : mach_port_t;
+ command : uint32_t;
+ ifname : string_t;
+ servicePort : uint16_t;
+ protocol : uint16_t;
+ ServerAuditToken token : audit_token_t);
#include <time.h> // platform support for UTC time
#include <arpa/inet.h> // for inet_aton
#include <pthread.h>
+#include <netdb.h> // for getaddrinfo
#include <netinet/in.h> // For IP_RECVTTL
#ifndef IP_RECVTTL
#include <IOKit/IOKitLib.h>
#include <IOKit/IOMessage.h>
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
// This is currently defined in IOKit/PrivateHeaders/IOKitLibPrivate.h. Till it becomes an Public
// API, we will have our own declaration
void IONotificationPortSetDispatchQueue(IONotificationPortRef notify, dispatch_queue_t queue);
#include <mach/mach_port.h>
#include <mach/mach_time.h>
#include "helper.h"
+#include "P2PPacketFilter.h"
#include <asl.h>
+#include <SystemConfiguration/SCDynamicStorePrivate.h>
+
#if APPLE_OSX_mDNSResponder
#include <DeviceToDeviceManager/DeviceToDeviceManager.h>
#include <AWACS.h>
// We currently do not offer sleep proxy service on laptops, or on machines that are set to go to sleep.
mDNSexport int OfferSleepProxyService = 0;
mDNSexport int DisableSleepProxyClient = 0;
+mDNSexport int UseInternalSleepProxy = 1; // Set to non-zero to use internal (in-NIC) Sleep Proxy
// We disable inbound relay connection if this value is set to true (typically via command-line switch).
mDNSBool DisableInboundRelayConnection = mDNSfalse;
-mDNSexport int OSXVers;
+mDNSexport int OSXVers, iOSVers;
mDNSexport int KQueueFD;
#ifndef NO_SECURITYFRAMEWORK
mDNSexport int WatchDogReportingThreshold = 250;
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
dispatch_queue_t SSLqueue;
#endif
mDNSexport mDNSAddr ActiveDirectoryPrimaryDomainServer;
#endif // APPLE_OSX_mDNSResponder
+// Used by AutoTunnel
+const char btmmprefix[] = "btmmdns:";
+const char dnsprefix[] = "dns:";
+
// ***************************************************************************
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
{ LogMsg("DNSNameCompressionParseBytes: failed to get large RR"); m->rec.r.resrec.RecordType = 0; return mStatus_UnknownErr; }
else LogInfo("DNSNameCompressionParseBytes: got rr: %s", CRDisplayString(m, &m->rec.r));
- mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSInterface_P2P, m->rec.r.resrec.rrtype, 7200, kDNSRecordTypeShared, FreeD2DARElemCallback, NULL);
+ mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSInterface_P2P, m->rec.r.resrec.rrtype, 7200, kDNSRecordTypeShared, AuthRecordP2P, FreeD2DARElemCallback, NULL);
AssignDomainName(&rr->namestorage, &m->rec.namestorage);
rr->resrec.rdlength = m->rec.r.resrec.rdlength;
rr->resrec.rdata->MaxRDLength = m->rec.r.resrec.rdlength;
mDNSPlatformMemFree(ptr);
return;
}
-
err = mDNS_Register(m, &ptr->ar);
if (err)
{
DomainnameToLower(resourceRecord->name, &lower);
LogInfo("external_start_advertising_service: %s", RRDisplayString(&mDNSStorage, resourceRecord));
+ // For SRV records, update packet filter if p2p interface already exists, otherwise,
+ // if will be updated when we get the KEV_DL_IF_ATTACHED event for the interface.
+ // Bonjour filter rules are removed when p2p interface KEV_DL_IF_DETACHED event is received.
+ if (resourceRecord->rrtype == kDNSType_SRV)
+ mDNSInitPacketFilter();
+
if (resourceRecord->rrtype == kDNSServiceType_A || resourceRecord->rrtype == kDNSServiceType_AAAA)
{
LogInfo("external_start_advertising_service: ignoring address record");
}
-mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id)
+mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id, mDNSBool suppressNetworkChange)
{
NetworkInterfaceInfoOSX *i;
if (id == mDNSInterface_LocalOnly) return(kDNSServiceInterfaceIndexLocalOnly);
for (i = m->p->InterfaceList; i; i = i->next)
if (i->scope_id == scope_id) return(i->scope_id);
+ // If we are supposed to suppress network change, return "id" back
+ if (suppressNetworkChange) return 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);
#if APPLE_OSX_mDNSResponder
mDNSexport void mDNSASLLog(uuid_t *uuid, const char *subdomain, const char *result, const char *signature, const char *fmt, ...)
{
- if (OSXVers < OSXVers_10_6_SnowLeopard) return;
+ if (OSXVers < OSXVers_10_6_SnowLeopard) return; // Only do ASL on Mac OS X 10.6 and later (not on iOS)
static char buffer[512];
aslmsg asl_msg = asl_new(ASL_TYPE_MSG);
if (info && mDNSAddrIsDNSMulticast(dst)) // Specify outgoing interface
{
err = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &info->scope_id, sizeof(info->scope_id));
- if (err < 0) LogMsg("setsockopt - IPV6_MULTICAST_IF error %d errno %d (%s)", err, errno, strerror(errno));
+ if (err < 0)
+ {
+ char name[IFNAMSIZ];
+ if (if_indextoname(info->scope_id, name) != NULL)
+ LogMsg("setsockopt - IPV6_MULTICAST_IF error %d errno %d (%s)", err, errno, strerror(errno));
+ else
+ LogInfo("setsockopt - IPV6_MUTLICAST_IF scopeid %d, not a valid interface", info->scope_id);
+ }
}
}
#endif
return(err);
}
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
mDNSlocal void doSSLHandshake(void *ctx)
{
TCPSocket *sock = (TCPSocket*)ctx;
mStatus err = SSLHandshake(sock->tlsContext);
- //Can't have multiple threads in mDNS core. When __LIB_DISPATCH__ is
+ //Can't have multiple threads in mDNS core. When MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM is
//defined, KQueueLock is a noop. Hence we need to serialize here
//
//NOTE: We just can't serialize doTcpSocketCallback alone on the main queue.
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);
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
// Dispatch it on a separate serial queue to avoid deadlocks with threads running on main queue
dispatch_async(SSLqueue, ^{doSSLHandshake(sock);});
doTcpSocketCallback(sock);
}
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
mDNSexport int KQueueSet(int fd, u_short flags, short filter, KQueueEntry *const entryRef)
{
dispatch_queue_t queue = dispatch_get_main_queue();
mDNSexport void mDNSPlatformCloseFD(KQueueEntry *kq, int fd)
{
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
(void)fd; //unused
if (kq->readSource)
{
if (err < 0) { LogMsg("ERROR: bind6 %s", strerror(errno)); return err; }
// We want to receive destination addresses and receive interface identifiers
- err = setsockopt(skt, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on));
- if (err < 0) { LogMsg("ERROR: setsockopt IPV6_PKTINFO %s", strerror(errno)); return err; }
+ err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
+ if (err < 0) { LogMsg("ERROR: setsockopt IPV6_RECVPKTINFO %s", strerror(errno)); return err; }
mDNSPlatformMemZero(&addr6, sizeof(addr6));
socklen_t len = sizeof(addr6);
k->KQcallback = tcpKQSocketCallback;
k->KQcontext = sock;
k->KQtask = "mDNSPlatformTCPSocket";
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
k->readSource = mDNSNULL;
k->writeSource = mDNSNULL;
k->fdClosed = mDNSfalse;
if (sock->handshake == handshake_in_progress) // SSLHandshake thread using this sock (esp. tlsContext)
{
LogInfo("mDNSPlatformTCPCloseConnection: called while handshake in progress");
+ // When we come back from SSLHandshake, we will notice that a close was here and
+ // call this function again which will do the cleanup then.
sock->handshake = handshake_to_be_closed;
- }
- if (sock->handshake == handshake_to_be_closed)
return;
+ }
SSLClose(sock->tlsContext);
SSLDisposeContext(sock->tlsContext);
if (mDNSSameIPPort(port, NATPMPAnnouncementPort)) { if (outport) *outport = zeroIPPort; return mStatus_NoError; }
// We want to receive destination addresses and receive interface identifiers
- err = setsockopt(skt, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on));
- if (err < 0) { errstr = "setsockopt - IPV6_PKTINFO"; goto fail; }
+ err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
+ if (err < 0) { errstr = "setsockopt - IPV6_RECVPKTINFO"; goto fail; }
// We want to receive packet hop count value so we can check it
- err = setsockopt(skt, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, sizeof(on));
- if (err < 0) { errstr = "setsockopt - IPV6_HOPLIMIT"; goto fail; }
+ err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on));
+ if (err < 0) { errstr = "setsockopt - IPV6_RECVHOPLIMIT"; goto fail; }
// We want to receive only IPv6 packets. Without this option we get IPv4 packets too,
// with mapped addresses of the form 0:0:0:0:0:FFFF:xxxx:xxxx, where xxxx:xxxx is the IPv4 address
k->KQcallback = myKQSocketCallBack;
k->KQcontext = cp;
k->KQtask = "UDP packet reception";
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
k->readSource = mDNSNULL;
k->writeSource = mDNSNULL;
k->fdClosed = mDNSfalse;
mDNSlocal void CloseBPF(NetworkInterfaceInfoOSX *const i)
{
LogSPS("%s closing BPF fd %d", i->ifinfo.ifname, i->BPF_fd);
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
// close will happen in the cancel handler
dispatch_source_cancel(i->BPF_source);
#else
exit:
KQueueUnlock(info->m, "bpf_callback");
}
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
mDNSlocal void bpf_callback_dispatch(NetworkInterfaceInfoOSX *const info)
{
bpf_callback_common(info);
i->BPF_len = sizeof(m->imsg);
if (ioctl(fd, BIOCSBLEN, &i->BPF_len) < 0)
LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCSBLEN failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno));
- else LogSPS("mDNSPlatformReceiveBPF_fd: %d %s BIOCSBLEN %d", i->BPF_len);
+ else
+ LogSPS("mDNSPlatformReceiveBPF_fd: %d %s BIOCSBLEN %d", fd, i->ifinfo.ifname, i->BPF_len);
}
static const u_int opt_one = 1;
{ LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCSETIF failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); i->BPF_fd = -3; }
else
{
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
i->BPF_fd = fd;
i->BPF_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, dispatch_get_main_queue());
if (!i->BPF_source) {LogMsg("mDNSPlatformReceiveBPF_fd: dispatch source create failed");return;}
mDNSSameAddress(&ip, &(*p)->ifinfo.ip) &&
mDNSSameEthAddress(&bssid, &(*p)->BSSID))
{
- debugf("AddInterfaceToList: Found existing interface %lu %.6a with address %#a at %p", scope_id, &bssid, &ip, *p);
+ debugf("AddInterfaceToList: Found existing interface %lu %.6a with address %#a at %p, ifname before %s, after %s", scope_id, &bssid, &ip, *p, (*p)->ifinfo.ifname, ifa->ifa_name);
+ // The name should be updated to the new name so that we don't report a wrong name in our SIGINFO output.
+ // When interfaces are created with same MAC address, kernel resurrects the old interface.
+ // Even though the interface index is the same (which should be sufficient), when we receive a UDP packet
+ // we get the corresponding name for the interface index on which the packet was received and check against
+ // the InterfaceList for a matching name. So, keep the name in sync
+ strlcpy((*p)->ifinfo.ifname, ifa->ifa_name, sizeof((*p)->ifinfo.ifname));
(*p)->Exists = mDNStrue;
// If interface was not in getifaddrs list last time we looked, but it is now, update 'AppearanceTime' for this record
if ((*p)->LastSeen != utc) (*p)->AppearanceTime = utc;
// local-only services, which need a loopback address record.
i->ifinfo.Advertise = m->DivertMulticastAdvertisements ? ((ifa->ifa_flags & IFF_LOOPBACK) ? mDNStrue : mDNSfalse) : m->AdvertiseLocalAddresses;
i->ifinfo.McastTxRx = mDNSfalse; // For now; will be set up later at the end of UpdateInterfaceList
+ i->ifinfo.Loopback = ((ifa->ifa_flags & IFF_LOOPBACK) != 0) ? mDNStrue : mDNSfalse;
i->next = mDNSNULL;
i->m = m;
mDNSlocal void RemoveAutoTunnelDomainStatus(const mDNS *const m, const DomainAuthInfo *const info)
{
char buffer[1024];
+ mDNSu32 buflen;
CFStringRef domain;
LogInfo("RemoveAutoTunnelDomainStatus: %##s", info->domain.c);
if (!domainStatusDict) { LogMsg("RemoveAutoTunnelDomainStatus: No domainStatusDict"); return; }
- buffer[mDNS_snprintf(buffer, sizeof(buffer), "%##s", info->domain.c) - 1] = 0;
+ buflen = mDNS_snprintf(buffer, sizeof(buffer), "%##s", info->domain.c);
+ if (info->AutoTunnel == dnsprefix) buffer[buflen-1] = 0; // Strip the trailing dot for Classic
domain = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8);
if (!domain) { LogMsg("RemoveAutoTunnelDomainStatus: Could not create CFString domain"); return; }
const NATTraversalInfo *const llq = m->LLQNAT.clientContext ? &m->LLQNAT : mDNSNULL;
const NATTraversalInfo *const tun = info->AutoTunnelNAT.clientContext ? &info->AutoTunnelNAT : mDNSNULL;
char buffer[1024];
+ mDNSu32 buflen = 0;
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFStringRef domain = NULL;
CFStringRef tmp = NULL;
CFNumberRef num = NULL;
mStatus status = mStatus_NoError;
+ mStatus llqStatus = mStatus_NoError;
+ char llqBuffer[1024];
if (!m->mDNS_busy) LogMsg("UpdateAutoTunnelDomainStatus: ERROR!! Lock not held");
if (!domainStatusDict)
if (!dict) { LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFDictionary dict"); return; }
- buffer[mDNS_snprintf(buffer, sizeof(buffer), "%##s", info->domain.c) - 1] = 0;
+ buflen = mDNS_snprintf(buffer, sizeof(buffer), "%##s", info->domain.c);
+ if (info->AutoTunnel == dnsprefix) buffer[buflen-1] = 0; // Strip the trailing dot for Classic
domain = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8);
if (!domain) { LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString domain"); return; }
}
}
- // If we have a relay address which lets other hosts to reach us through the relay, then we should
- // report success except for the LLQs they don't go over the relay connection. If LLQs fail, then
- // report failure. In future, when LLQs go over the relay connection, we don't need this logic
- if (!mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddrIn))
- {
- // If we have a bad signature error updating RR, it overrides any error as
- // the user needs to be notified immediately
- status = UpdateRRStatus(m, buffer, sizeof(buffer), info);
- if (status == mStatus_NoError)
- {
- status = UpdateLLQStatus(m, buffer, sizeof(buffer), info);
- if (status == mStatus_PollingMode)
- {
- // If we have a relay connection and we are in polling mode, report as success.
- // This normally happens when we are behind Double NAT or NAT with UPnP/NAT-PMP
- // disabled but we are able to successfully file share/screen share with the help
- // of the relay connection. As it just affects the discovery/update of the other
- // BTMM hosts, we consider it as minor issue and report it as success.
- LogInfo("UpdateAutoTunnelDomainStatus:NonzeroRelayAddress: Polling reported as success");
- status = mStatus_NoError;
- mDNS_snprintf(buffer, sizeof(buffer), "Success");
- }
- }
- LogInfo("UpdateAutoTunnelDomainStatus:NonzeroRelayAddress: Status %d, %s", status, buffer);
+ mDNS_snprintf(buffer, sizeof(buffer), "Success");
+ llqStatus = UpdateLLQStatus(m, llqBuffer, sizeof(llqBuffer), info);
+ status = UpdateRRStatus(m, buffer, sizeof(buffer), info);
+
+ // If we have a bad signature error updating a RR, it overrides any error as it needs to be
+ // reported so that it can be fixed automatically (or the user needs to be notified)
+ if (status != mStatus_NoError)
+ {
+ LogInfo("UpdateAutoTunnelDomainStatus: RR Status %d, %s", status, buffer);
+ }
+ else if (m->Router.type == mDNSAddrType_None)
+ {
+ status = mStatus_NoRouter;
+ mDNS_snprintf(buffer, sizeof(buffer), "No network connection - none");
+ }
+ else if (m->Router.type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsZero(m->Router.ip.v4))
+ {
+ status = mStatus_NoRouter;
+ mDNS_snprintf(buffer, sizeof(buffer), "No network connection - v4 zero");
+ }
+ else if (mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddrOut))
+ {
+ status = info->AutoTunnel == btmmprefix ? mStatus_ServiceNotRunning : mStatus_PollingMode;
+ mDNS_snprintf(buffer, sizeof(buffer), "No relay connection");
}
else if (!llq && !tun)
{
status = mStatus_NotInitializedErr;
mDNS_snprintf(buffer, sizeof(buffer), "Neither LLQ nor AutoTunnel NAT port mapping is currently active");
}
- else if ((llq && llq->Result == mStatus_DoubleNAT) || (tun && tun->Result == mStatus_DoubleNAT))
+ else if (llqStatus == mStatus_NoSuchRecord)
+ {
+ status = llqStatus;
+ mDNS_snprintf(buffer, sizeof(buffer), llqBuffer);
+ }
+ else if (info->AutoTunnel == btmmprefix && ((llq && llq->Result == mStatus_DoubleNAT) || (tun && tun->Result == mStatus_DoubleNAT)))
{
status = mStatus_DoubleNAT;
mDNS_snprintf(buffer, sizeof(buffer), "Double NAT: Router is reporting an external address");
}
- else if ((llq && llq->Result == mStatus_NATPortMappingDisabled) || (tun && tun->Result == mStatus_NATPortMappingDisabled) ||
- (m->LastNATMapResultCode == NATErr_Refused && ((llq && !llq->Result && mDNSIPPortIsZero(llq->ExternalPort)) || (tun && !tun->Result && mDNSIPPortIsZero(tun->ExternalPort)))))
+ else if (info->AutoTunnel == btmmprefix && ((llq && llq->Result == mStatus_NATPortMappingDisabled) || (tun && tun->Result == mStatus_NATPortMappingDisabled) ||
+ (m->LastNATMapResultCode == NATErr_Refused && ((llq && !llq->Result && mDNSIPPortIsZero(llq->ExternalPort)) || (tun && !tun->Result && mDNSIPPortIsZero(tun->ExternalPort))))))
{
status = mStatus_NATPortMappingDisabled;
mDNS_snprintf(buffer, sizeof(buffer), "NAT-PMP is disabled on the router");
}
- else if ((llq && llq->Result) || (tun && tun->Result))
+ else if (info->AutoTunnel == btmmprefix && ((llq && llq->Result) || (tun && tun->Result)))
{
status = mStatus_NATTraversal;
mDNS_snprintf(buffer, sizeof(buffer), "Error obtaining NAT port mapping from router");
}
- else if (m->Router.type == mDNSAddrType_None)
- {
- status = mStatus_NoRouter;
- mDNS_snprintf(buffer, sizeof(buffer), "No network connection - none");
- }
- else if (m->Router.type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsZero(m->Router.ip.v4))
- {
- status = mStatus_NoRouter;
- mDNS_snprintf(buffer, sizeof(buffer), "No network connection - v4 zero");
- }
- else if ((llq && mDNSIPPortIsZero(llq->ExternalPort)) || (tun && mDNSIPPortIsZero(tun->ExternalPort)))
+ else if (info->AutoTunnel == btmmprefix && ((llq && mDNSIPPortIsZero(llq->ExternalPort)) || (tun && mDNSIPPortIsZero(tun->ExternalPort))))
{
status = mStatus_NATTraversal;
mDNS_snprintf(buffer, sizeof(buffer), "Unable to obtain NAT port mapping from router");
}
+ else if (info->AutoTunnel == btmmprefix || llqStatus != mStatus_PollingMode)
+ {
+ status = llqStatus;
+ mDNS_snprintf(buffer, sizeof(buffer), llqBuffer);
+ LogInfo("UpdateAutoTunnelDomainStatus: LLQ Status %d, %s", status, buffer);
+ }
else
{
- status = UpdateRRStatus(m, buffer, sizeof(buffer), info);
- if (status == mStatus_NoError)
- status = UpdateLLQStatus(m, buffer, sizeof(buffer), info);
- LogInfo("UpdateAutoTunnelDomainStatus:ZeroRelayAddress: Status %d, %s", status, buffer);
+ mDNS_snprintf(buffer, sizeof(buffer), "Polling success");
}
num = CFNumberCreate(NULL, kCFNumberSInt32Type, &status);
{
AnonymousRacoonConfig = info;
// Create or revert configuration file, and start (or SIGHUP) Racoon
- (void)mDNSConfigureServer(AnonymousRacoonConfig ? kmDNSUp : kmDNSDown, AnonymousRacoonConfig ? &AnonymousRacoonConfig->domain : mDNSNULL);
+ (void)mDNSConfigureServer(AnonymousRacoonConfig ? kmDNSUp : kmDNSDown, AnonymousRacoonConfig ? AnonymousRacoonConfig->AutoTunnel : mDNSNULL, AnonymousRacoonConfig ? &AnonymousRacoonConfig->domain : mDNSNULL);
}
}
return;
}
- // if disabled administratively, don't register
// if disabled administratively, don't register
if (!m->RegisterAutoTunnel6 || DisableInboundRelayConnection)
{
{
LogInfo("AutoTunnelHostNameChanged %#s.%##s", m->hostlabel.c, info->domain.c);
+#ifndef NO_SECURITYFRAMEWORK
DeregisterAutoTunnelDevInfoRecord(m, info);
+#endif
DeregisterAutoTunnelServiceRecords(m, info);
DeregisterAutoTunnel6Record(m, info);
RegisterAutoTunnelServiceRecords(m, info);
if (info->AutoTunnelDeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered)
{
- mDNS_SetupResourceRecord(&info->AutoTunnelDeviceInfo, mDNSNULL, mDNSInterface_Any, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnregistered, AutoTunnelRecordCallback, info);
+ mDNS_SetupResourceRecord(&info->AutoTunnelDeviceInfo, mDNSNULL, mDNSInterface_Any, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info);
RegisterAutoTunnelDevInfoRecord(m, info);
}
if (info->AutoTunnel6Record.resrec.RecordType == kDNSRecordTypeUnregistered)
{
- mDNS_SetupResourceRecord(&info->AutoTunnel6Record, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL, kDNSRecordTypeUnregistered, AutoTunnelRecordCallback, info);
+ mDNS_SetupResourceRecord(&info->AutoTunnel6Record, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL, kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info);
RegisterAutoTunnel6Record(m, info);
}
AbortDeregistration(m, &info->AutoTunnelHostRecord);
mDNS_SetupResourceRecord(&info->AutoTunnelTarget, mDNSNULL, mDNSInterface_Any, kDNSType_A, kHostNameTTL,
- kDNSRecordTypeUnregistered, AutoTunnelRecordCallback, info);
+ kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info);
mDNS_SetupResourceRecord(&info->AutoTunnelService, mDNSNULL, mDNSInterface_Any, kDNSType_SRV, kHostNameTTL,
- kDNSRecordTypeUnregistered, AutoTunnelRecordCallback, info);
+ kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info);
mDNS_SetupResourceRecord(&info->AutoTunnelHostRecord, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL,
- kDNSRecordTypeUnregistered, AutoTunnelRecordCallback, info);
+ kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info);
// Try to get a NAT port mapping for the AutoTunnelService
info->AutoTunnelNAT.clientCallback = AutoTunnelNATCallback;
rmt_outer6.b[3] = tun->rmt_outer.b[3];
}
- return(mDNSAutoTunnelSetKeys(AddNew ? kmDNSAutoTunnelSetKeysReplace : kmDNSAutoTunnelSetKeysDelete, tun->loc_inner.b, loc_outer6.b, kRacoonPort, tun->rmt_inner.b, rmt_outer6.b, mDNSVal16(tun->rmt_outer_port), SkipLeadingLabels(&tun->dstname, 1)));
+ return(mDNSAutoTunnelSetKeys(AddNew ? kmDNSAutoTunnelSetKeysReplace : kmDNSAutoTunnelSetKeysDelete, tun->loc_inner.b, loc_outer6.b, kRacoonPort, tun->rmt_inner.b, rmt_outer6.b, mDNSVal16(tun->rmt_outer_port), tun->prefix, SkipLeadingLabels(&tun->dstname, 1)));
}
// If the EUI-64 part of the IPv6 ULA matches, then that means the two addresses point to the same machine
{
ClientTunnel *p = mallocL("ClientTunnel", sizeof(ClientTunnel));
if (!p) return;
+ p->prefix = q->AuthInfo->AutoTunnel;
AssignDomainName(&p->dstname, &q->qname);
p->MarkedForDeletion = mDNSfalse;
p->loc_inner = zerov6Addr;
p->q.ForceMCast = mDNSfalse;
p->q.ReturnIntermed = mDNStrue;
p->q.SuppressUnusable = mDNSfalse;
- p->q.WakeOnResolve = mDNSfalse;
+ p->q.SearchListIndex = 0;
+ p->q.AppendSearchDomains = 0;
+ p->q.RetryWithSearchDomains = mDNSfalse;
+ p->q.TimeoutQuestion = 0;
+ p->q.WakeOnResolve = 0;
+ p->q.qnameOrig = mDNSNULL;
p->q.QuestionCallback = AutoTunnelCallback;
p->q.QuestionContext = p;
int InfoSocket = socket(AF_INET6, SOCK_DGRAM, 0);
if (InfoSocket < 3 && errno != EAFNOSUPPORT) LogMsg("UpdateInterfaceList: InfoSocket error %d errno %d (%s)", InfoSocket, errno, strerror(errno));
#endif
+
+ // During wakeup, we may get a network change notification e.g., new addresses, before we get
+ // a wake notification. This means that we have not set AnnounceOwner. Registering interfaces with
+ // core would cause us to probe again which will conflict with the sleep proxy server, if we had
+ // registered with it when going to sleep. Hence, need to delay until we get the wake notification
+
if (m->SleepState == SleepState_Sleeping) ifa = NULL;
while (ifa)
// If the interface is an old one that went away and came back in less than a minute, then we're in a flapping scenario.
i->Occulting = !(i->ifa_flags & IFF_LOOPBACK) && (utc - i->LastSeen > 0 && utc - i->LastSeen < 60);
- mDNS_RegisterInterface(m, n, i->Flashing && i->Occulting);
+ // Temporary fix to handle P2P flapping. P2P reuses the scope-id, mac address and the IP address
+ // everytime it creates a new interface. We think it is a duplicate and hence consider it
+ // as flashing and occulting, that is, flapping. If an interface is marked as flapping,
+ // mDNS_RegisterInterface() changes the probe delay from 1/2 second to 5 seconds and
+ // logs a warning message to system.log noting frequent interface transitions.
+ if (strncmp(i->ifinfo.ifname, "p2p", 3) == 0)
+ {
+ LogInfo("SetupActiveInterfaces: P2P %s interface registering %s %s", i->ifinfo.ifname,
+ i->Flashing ? " (Flashing)" : "",
+ i->Occulting ? " (Occulting)" : "");
+ mDNS_RegisterInterface(m, n, 0);
+ }
+ else
+ {
+ mDNS_RegisterInterface(m, n, i->Flashing && i->Occulting);
+ }
if (!mDNSAddressIsLinkLocal(&n->ip)) count++;
LogInfo("SetupActiveInterfaces: Registered %5s(%lu) %.6a InterfaceID %p(%p), primary %p, %#a/%d%s%s%s",
i->Flashing ? " (Flashing)" : "",
i->Occulting ? " (Occulting)" : "",
i->ifinfo.InterfaceActive ? " (Primary)" : "");
- mDNS_DeregisterInterface(m, &i->ifinfo, i->Flashing && i->Occulting);
+
+ // Temporary fix to handle P2P flapping. P2P reuses the scope-id, mac address and the IP address
+ // everytime it creates a new interface. We think it is a duplicate and hence consider it
+ // as flashing and occulting. The "core" does not flush the cache for this case. This leads to
+ // stale data returned to the application even after the interface is removed. The application
+ // then starts to send data but the new interface is not yet created.
+ if (strncmp(i->ifinfo.ifname, "p2p", 3) == 0)
+ {
+ LogInfo("ClearInactiveInterfaces: P2P %s interface deregistering %s %s", i->ifinfo.ifname,
+ i->Flashing ? " (Flashing)" : "",
+ i->Occulting ? " (Occulting)" : "");
+ mDNS_DeregisterInterface(m, &i->ifinfo, 0);
+ }
+ else
+ {
+ mDNS_DeregisterInterface(m, &i->ifinfo, i->Flashing && i->Occulting);
+ }
if (!mDNSAddressIsLinkLocal(&i->ifinfo.ip)) count++;
i->Registered = mDNSNULL;
// Note: If i->Registered is set, that means we've called mDNS_RegisterInterface() for this interface,
return (a->search_order < b->search_order) ? -1 : (a->search_order == b->search_order) ? 0 : 1;
}
+// ConfigResolvers is called twice - once to parse the "scoped_resolver" list and second time to parse the "resolver" list.
+// "scoped_resolver" has entries that should only be used for "scoped_questions" (for questions that specify an interface index
+// q->InterfaceID) and "resolver" entries should only be used for non-scoped questions. Entries in either of the list can specify
+// an ifindex. This means that the dns query should be scoped to that interface when sent out on the wire. The flag value
+// "DNS_RESOLVER_FLAGS_SCOPED" itself appears only in "scoped" list of resolvers.
+//
+// Before "scoped_resolver" was introduced, the entries in "resolver" list can contain options like "interface=en0" which
+// was meant to scope the query (non-scoped queries) to a specific interface. We still support this option. On top of that,
+// we also support a new way of specifying the interface index as described above.
mDNSlocal void ConfigResolvers(mDNS *const m, dns_config_t *config, mDNSBool scope, mDNSBool setsearch, mDNSBool setservers)
{
- int i;
+ int i, j;
domainname d;
#if DNSINFO_VERSION >= 20091104
dns_resolver_t **resolver = scope ? config->scoped_resolver : config->resolver;
int nresolvers = config->n_resolver;
#endif
- // Currently we don't support search lists for scoped resolvers. The WAB support for this will be added later.
if (setsearch && !scope && nresolvers)
{
// Due to the vagaries of Apple's SystemConfiguration and dnsinfo.h APIs, if there are no search domains
if (resolver[0]->n_search == 0)
{
LogInfo("ConfigResolvers: (%s) configuring zeroth domain as search list %s", scope ? "Scoped" : "Non-scoped", resolver[0]->domain);
- mDNS_AddSearchDomain_CString(resolver[0]->domain);
+ mDNS_AddSearchDomain_CString(resolver[0]->domain, mDNSNULL);
}
else
{
for (i = 0; i < resolver[0]->n_search; i++)
{
LogInfo("ConfigResolvers: (%s) configuring search list %s", scope ? "Scoped" : "Non-scoped", resolver[0]->search[i]);
- mDNS_AddSearchDomain_CString(resolver[0]->search[i]);
+ mDNS_AddSearchDomain_CString(resolver[0]->search[i], mDNSNULL);
}
}
}
- if (!setservers) return;
+ // scoped search domains are set below. If neither scoped nor setting servers, we have nothing to do
+ if (!scope && !setservers) return;
// For the "default" resolver ("resolver #1") the "domain" value is bogus and we need to ignore it.
// e.g. the default resolver's "domain" value might say "apple.com", which indicates that this resolver
// Note: Unlike the BSD Sockets APIs (where TCP and UDP port numbers are universally in network byte order)
// in Apple's "dnsinfo.h" API the port number is declared to be a "uint16_t in host byte order"
// We also don't need to do any more work if there are no nameserver addresses
- if (r->port == 5353 || r->n_nameserver == 0) continue;
+ if (r->port == 5353 || r->n_nameserver == 0)
+ {
+ char *opt = r->options;
+ if (opt && !strncmp(opt, "mdns", strlen(opt)))
+ {
+ if (!MakeDomainNameFromDNSNameString(&d, r->domain))
+ { LogMsg("ConfigResolvers: config->resolver[%d] bad domain %s", i, r->domain); continue; }
+ mDNS_AddMcastResolver(m, &d, interface, r->timeout);
+ }
+ continue;
+ }
+
if (!r->domain || !*r->domain) d.c[0] = 0;
else if (!MakeDomainNameFromDNSNameString(&d, r->domain))
}
}
- // flags and if_index are defined only from this DNSINFO_VERSION onwards. Currently these
- // fields are zero for non-scoped resolvers. For now, these fields have non-zero values only
- // for scoped_resolvers.
+ // flags and if_index are defined only from this DNSINFO_VERSION onwards.
+ // Parse the interface index if we have not already parsed one above.
#if DNSINFO_VERSION >= 20091104
- if (scope && (r->flags & DNS_RESOLVER_FLAGS_SCOPED) && (r->if_index != 0))
+ if ((interface == mDNSInterface_Any) && (r->if_index != 0))
{
NetworkInterfaceInfoOSX *ni;
interface = mDNSNULL;
}
#endif
+ if (setsearch)
+ {
+ // For non-scoped resolvers unlike scoped resolvers, only zeroth resolver has search lists if any. For scoped
+ // resolvers, we need to parse all the entries.
+ if (scope)
+ {
+ for (j = 0; j < resolver[i]->n_search; j++)
+ {
+ LogInfo("ConfigResolvers: (%s) configuring search list %s, Interface %p", scope ? "Scoped" : "Non-scoped", resolver[i]->search[j], interface);
+ mDNS_AddSearchDomain_CString(resolver[i]->search[j], interface);
+ }
+ // Parse other scoped resolvers for search lists
+ if (!setservers) continue;
+ }
+ }
+
for (n = 0; n < r->n_nameserver; n++)
if (r->nameserver[n]->sa_family == AF_INET || r->nameserver[n]->sa_family == AF_INET6)
{
DNSServer *s;
#if DNSINFO_VERSION >= 20091104
// By setting scoped, this DNSServer can only be picked if the right interfaceID
- // is given in the question
+ // is given in the question.
if (scope && (r->flags & DNS_RESOLVER_FLAGS_SCOPED) && (interface == mDNSNULL))
- LogMsg("ConfigResolvers: ERROR: scoped is set but if_index %d is invalid for DNSServer %#a:%d", r->if_index, &saddr, mDNSVal16(r->port ? mDNSOpaque16fromIntVal(r->port) : UnicastDNSPort));
+ LogMsg("ConfigResolvers: ERROR: scoped is set but if_index %d is invalid for DNSServer %#a:%d",
+ r->if_index, &saddr, mDNSVal16(r->port ? mDNSOpaque16fromIntVal(r->port) : UnicastDNSPort));
else
scopedDNS = (scope && (r->flags & DNS_RESOLVER_FLAGS_SCOPED)) ? mDNStrue : mDNSfalse;
#endif
- s = mDNS_AddDNSServer(m, &d, interface, &saddr, r->port ? mDNSOpaque16fromIntVal(r->port) : UnicastDNSPort, scopedDNS);
+ // The timeout value is for all the DNS servers in a given resolver, hence we pass
+ // the timeout value only for the first DNSServer. If we don't have a value in the
+ // resolver, then use the core's default value
+ //
+ // Note: this assumes that when the core picks a list of DNSServers for a question,
+ // it takes the sum of all the timeout values for all DNS servers. By doing this, it
+ // tries all the DNS servers in a specified timeout
+ s = mDNS_AddDNSServer(m, &d, interface, &saddr, r->port ? mDNSOpaque16fromIntVal(r->port) : UnicastDNSPort, scopedDNS,
+ (n == 0 ? (r->timeout ? r->timeout : DEFAULT_UDNS_TIMEOUT) : 0));
if (s)
{
if (disabled) s->teststate = DNSServer_Disabled;
- LogInfo("ConfigResolvers: DNS server %#a:%d for domain %##s from slot %d,%d", &s->addr, mDNSVal16(s->port), d.c, i, n);
+ LogInfo("ConfigResolvers: DNS server %#a:%d for domain %##s from slot %d, %d",
+ &s->addr, mDNSVal16(s->port), d.c, i, n);
}
}
}
a.ip.v4.b[2] & n.ip.v4.b[2],
a.ip.v4.b[1] & n.ip.v4.b[1],
a.ip.v4.b[0] & n.ip.v4.b[0]);
- mDNS_AddSearchDomain_CString(buf);
+ mDNS_AddSearchDomain_CString(buf, mDNSNULL);
}
ifa = ifa->ifa_next;
}
// Apparently this is expected behaviour -- "not a bug".
// Accordingly, we suppress syslog messages for the first three minutes after boot.
// If we are still getting failures after three minutes, then we log them.
- if (OSXVers > OSXVers_10_3_Panther && (mDNSu32)mDNSPlatformRawTime() > (mDNSu32)(mDNSPlatformOneSecond * 180))
+ if ((mDNSu32)mDNSPlatformRawTime() > (mDNSu32)(mDNSPlatformOneSecond * 180))
LogMsg("mDNSPlatformSetDNSConfig: Error: dns_configuration_copy returned NULL");
}
else
ConfigResolvers(m, config, mDNStrue, setsearch, setservers);
#endif
dns_configuration_free(config);
+ if (setsearch) RetrySearchDomainQuestions(m);
setservers = mDNSfalse; // Done these now -- no need to fetch the same data from SCDynamicStore
setsearch = mDNSfalse;
}
inet_aton(buf, (struct in_addr *) &addr.ip.v4))
{
LogInfo("Adding DNS server from dict: %s", buf);
- mDNS_AddDNSServer(m, mDNSNULL, mDNSInterface_Any, &addr, UnicastDNSPort, mDNSfalse);
+ mDNS_AddDNSServer(m, mDNSNULL, mDNSInterface_Any, &addr, UnicastDNSPort, mDNSfalse, 0);
}
}
}
{
CFStringRef s = CFArrayGetValueAtIndex(searchDomains, i);
if (s && CFStringGetCString(s, buf, sizeof(buf), kCFStringEncodingUTF8))
- mDNS_AddSearchDomain_CString(buf);
+ mDNS_AddSearchDomain_CString(buf, mDNSNULL);
}
}
else // No kSCPropNetDNSSearchDomains, so use kSCPropNetDNSDomainName
// (and the domain from the "domain" field will also appear somewhere in that list).
CFStringRef string = CFDictionaryGetValue(dict, kSCPropNetDNSDomainName);
if (string && CFStringGetCString(string, buf, sizeof(buf), kCFStringEncodingUTF8))
- mDNS_AddSearchDomain_CString(buf);
+ mDNS_AddSearchDomain_CString(buf, mDNSNULL);
}
+ RetrySearchDomainQuestions(m);
}
CFRelease(dict);
}
if (!sa) { LogMsg("SetDomainSecrets: CFArrayCreateMutable failed"); return; }
CFIndex i;
CFDataRef data = NULL;
- const int itemsPerEntry = 3; // domain name, key name, key value
+ const int itemsPerEntry = 4; // domain name, key name, key value, Name value
CFArrayRef secrets = NULL;
int err = mDNSKeychainGetSecrets(&secrets);
if (err || !secrets)
// Iterate through the secrets
for (i = 0; i < ArrayCount; ++i)
{
- int j;
+ mDNSBool AutoTunnel;
+ int j, offset;
CFArrayRef entry = CFArrayGetValueAtIndex(secrets, i);
if (CFArrayGetTypeID() != CFGetTypeID(entry) || itemsPerEntry != CFArrayGetCount(entry))
- { LogMsg("SetDomainSecrets: malformed entry"); continue; }
+ { LogMsg("SetDomainSecrets: malformed entry %d, itemsPerEntry %d", i, itemsPerEntry); continue; }
for (j = 0; j < CFArrayGetCount(entry); ++j)
if (CFDataGetTypeID() != CFGetTypeID(CFArrayGetValueAtIndex(entry, j)))
- { LogMsg("SetDomainSecrets: malformed entry item"); continue; }
+ { LogMsg("SetDomainSecrets: malformed entry item %d", j); continue; }
// The names have already been vetted by the helper, but checking them again here helps humans and automated tools verify correctness
- // Get DNS domain this key is for
- char stringbuf[MAX_ESCAPED_DOMAIN_NAME]; // Max legal domainname as C-string, including terminating NUL
- data = CFArrayGetValueAtIndex(entry, 0);
+ // Max legal domainname as C-string, including space for btmmprefix and terminating NUL
+ // Get DNS domain this key is for (kmDNSKcWhere)
+ char stringbuf[MAX_ESCAPED_DOMAIN_NAME + sizeof(btmmprefix)];
+ data = CFArrayGetValueAtIndex(entry, kmDNSKcWhere);
if (CFDataGetLength(data) >= (int)sizeof(stringbuf))
{ LogMsg("SetDomainSecrets: Bad kSecServiceItemAttr length %d", CFDataGetLength(data)); continue; }
CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (UInt8 *)stringbuf);
stringbuf[CFDataGetLength(data)] = '\0';
+ AutoTunnel = mDNSfalse;
+ offset = 0;
+ if (!strncmp(stringbuf, dnsprefix, strlen(dnsprefix)))
+ offset = strlen(dnsprefix);
+ else if (!strncmp(stringbuf, btmmprefix, strlen(btmmprefix)))
+ {
+ AutoTunnel = mDNStrue;
+ offset = strlen(btmmprefix);
+ }
domainname domain;
- if (!MakeDomainNameFromDNSNameString(&domain, stringbuf)) { LogMsg("SetDomainSecrets: bad key domain %s", stringbuf); continue; }
+ if (!MakeDomainNameFromDNSNameString(&domain, stringbuf + offset)) { LogMsg("SetDomainSecrets: bad key domain %s", stringbuf); continue; }
- // Get key name
- data = CFArrayGetValueAtIndex(entry, 1);
+ // Get key name (kmDNSKcAccount)
+ data = CFArrayGetValueAtIndex(entry, kmDNSKcAccount);
if (CFDataGetLength(data) >= (int)sizeof(stringbuf))
{ LogMsg("SetDomainSecrets: Bad kSecAccountItemAttr length %d", CFDataGetLength(data)); continue; }
CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8 *)stringbuf);
domainname keyname;
if (!MakeDomainNameFromDNSNameString(&keyname, stringbuf)) { LogMsg("SetDomainSecrets: bad key name %s", stringbuf); continue; }
- // Get key data
- data = CFArrayGetValueAtIndex(entry, 2);
+ // Get key data (kmDNSKcKey)
+ data = CFArrayGetValueAtIndex(entry, kmDNSKcKey);
if (CFDataGetLength(data) >= (int)sizeof(stringbuf))
{ LogMsg("SetDomainSecrets: Shared secret too long: %d", CFDataGetLength(data)); continue; }
CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (UInt8 *)stringbuf);
stringbuf[CFDataGetLength(data)] = '\0'; // mDNS_SetSecretForDomain requires NULL-terminated C string for key
+ // Get the Name of the keychain entry (kmDNSKcName) host or host:port
+ // The hostname also has the port number and ":". It should take a maximum of 6 bytes.
+ char hostbuf[MAX_ESCAPED_DOMAIN_NAME + 6]; // Max legal domainname as C-string, including terminating NUL
+ data = CFArrayGetValueAtIndex(entry, kmDNSKcName);
+ if (CFDataGetLength(data) >= (int)sizeof(hostbuf))
+ { LogMsg("SetDomainSecrets: Shared secret too long: %d", CFDataGetLength(data)); continue; }
+ CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8 *)hostbuf);
+ hostbuf[CFDataGetLength(data)] = '\0';
+
+ domainname hostname;
+ mDNSIPPort port;
+ char *hptr;
+ hptr = strchr(hostbuf, ':');
+
+ port.NotAnInteger = 0;
+ if (hptr)
+ {
+ mDNSu8 *p;
+ mDNSu16 val = 0;
+
+ *hptr++ = '\0';
+ while(hptr && *hptr != 0)
+ {
+ if (*hptr < '0' || *hptr > '9')
+ { LogMsg("SetDomainSecrets: Malformed Port number %d, val %d", *hptr, val); val = 0; break;}
+ val = val * 10 + *hptr - '0';
+ hptr++;
+ }
+ if (!val) continue;
+ p = (mDNSu8 *)&val;
+ port.NotAnInteger = p[0] << 8 | p[1];
+ }
+ // The hostbuf is of the format dsid@hostname:port. We don't care about the dsid.
+ hptr = strchr(hostbuf, '@');
+ if (hptr)
+ hptr++;
+ else
+ hptr = hostbuf;
+ if (!MakeDomainNameFromDNSNameString(&hostname, hptr)) { LogMsg("SetDomainSecrets: bad host name %s", hptr); continue; }
+
DomainAuthInfo *FoundInList;
for (FoundInList = m->AuthInfoList; FoundInList; FoundInList = FoundInList->next)
if (SameDomainName(&FoundInList->domain, &domain)) break;
// Uncomment the line below to view the keys as they're read out of the system keychain
// DO NOT SHIP CODE THIS WAY OR YOU'LL LEAK SECRET DATA INTO A PUBLICLY READABLE FILE!
- //LogInfo("SetDomainSecrets: %##s %##s %s", &domain.c, &keyname.c, stringbuf);
+ //LogInfo("SetDomainSecrets: domain %##s keyname %##s key %s hostname %##s port %d", &domain.c, &keyname.c, stringbuf, hostname.c, (port.b[0] << 8 | port.b[1]));
+ LogInfo("SetDomainSecrets: domain %##s keyname %##s hostname %##s port %d", &domain.c, &keyname.c, hostname.c, (port.b[0] << 8 | port.b[1]));
// If didn't find desired domain in the list, make a new entry
ptr = FoundInList;
if (!ptr) { LogMsg("SetDomainSecrets: No memory"); continue; }
}
- LogInfo("SetDomainSecrets: %d of %d %##s", i, ArrayCount, &domain);
- if (mDNS_SetSecretForDomain(m, ptr, &domain, &keyname, stringbuf, IsTunnelModeDomain(&domain)) == mStatus_BadParamErr)
+ //LogInfo("SetDomainSecrets: %d of %d %##s", i, ArrayCount, &domain);
+
+ // It is an AutoTunnel if the keychains tells us so (with btmm prefix) or if it is a TunnelModeDomain
+ if (mDNS_SetSecretForDomain(m, ptr, &domain, &keyname, stringbuf, &hostname, &port, (AutoTunnel ? btmmprefix : (IsTunnelModeDomain(&domain) ? dnsprefix : NULL))) == mStatus_BadParamErr)
{
if (!FoundInList) mDNSPlatformMemFree(ptr); // If we made a new DomainAuthInfo here, and it turned out bad, dispose it immediately
continue;
SCPreferencesSynchronize(m->p->SCPrefs);
CFDictionaryRef dict = SCPreferencesGetValue(m->p->SCPrefs, CFSTR("NAT"));
mDNSBool natenabled = (dict && (CFGetTypeID(dict) == CFDictionaryGetTypeID()) && DictionaryIsEnabled(dict));
- mDNSu8 sps = natenabled ? 50 : (OfferSleepProxyService && GetSystemSleepTimerSetting() == 0) ? OfferSleepProxyService : 0;
+ mDNSu8 sps = natenabled ? mDNSSleepProxyMetric_PrimarySoftware :
+ (OfferSleepProxyService && GetSystemSleepTimerSetting() == 0) ? mDNSSleepProxyMetric_IncidentalSoftware : 0;
// For devices that are not running NAT, but are set to never sleep, we may choose to act
// as a Sleep Proxy, but only for non-portable Macs (Portability > 35 means nominal weight < 3kg)
- if (sps > 50 && SPMetricPortability > 35) sps = 0;
+ //if (sps > mDNSSleepProxyMetric_PrimarySoftware && SPMetricPortability > 35) sps = 0;
// If we decide to let laptops act as Sleep Proxy, we should do it only when running on AC power, not on battery
- // For devices that are unable to sleep at all to save power (e.g. the current Apple TV hardware)
+ // For devices that are unable to sleep at all to save power, or save 1W or less by sleeping,
// it makes sense for them to offer low-priority Sleep Proxy service on the network.
// We rate such a device as metric 70 ("Incidentally Available Hardware")
- if (SPMetricMarginalPower == 10 && (!sps || sps > 70)) sps = 70;
+ if (SPMetricMarginalPower <= 60 && !sps) sps = mDNSSleepProxyMetric_IncidentalHardware;
+
+ // If the launchd plist specifies an explicit value for the Intent Metric, then use that instead of the
+ // computed value (currently 40 "Primary Network Infrastructure Software" or 80 "Incidentally Available Software")
+ if (sps && OfferSleepProxyService && OfferSleepProxyService < 100) sps = OfferSleepProxyService;
mDNSCoreBeSleepProxyServer(m, sps, SPMetricPortability, SPMetricMarginalPower, SPMetricTotalPower);
}
if (!SCPreferencesSetCallback(SCPrefs, InternetSharingChanged, &context))
{ LogMsg("SCPreferencesSetCallback failed: %s", SCErrorString(SCError())); CFRelease(SCPrefs); return(mStatus_NoMemoryErr); }
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
if (!SCPreferencesSetDispatchQueue( SCPrefs, dispatch_get_main_queue()))
{ LogMsg("SCPreferencesSetDispatchQueue failed: %s", SCErrorString(SCError())); return(mStatus_NoMemoryErr); }
#else
}
#define TfrRecordToNIC(RR) \
- (((RR)->resrec.InterfaceID && (RR)->resrec.InterfaceID != mDNSInterface_LocalOnly) || \
- (!(RR)->resrec.InterfaceID && ((RR)->ForceMCast || IsLocalDomain((RR)->resrec.name))))
+ ((!(RR)->resrec.InterfaceID && ((RR)->ForceMCast || IsLocalDomain((RR)->resrec.name))))
mDNSlocal mDNSu32 CountProxyRecords(mDNS *const m, uint32_t *const numbytes)
{
if (CFGetTypeID(ref) != CFStringGetTypeID() || !CFEqual(ref, CFSTR(mDNS_IOREG_VALUE)))
LogMsg("ActivateLocalProxy: mDNS_IOREG_KEY for interface %s/%s/%s value %s != %s",
ifname, n1, n2, CFStringGetCStringPtr(ref, mDNSNULL), mDNS_IOREG_VALUE);
+ else if (!UseInternalSleepProxy)
+ LogSPS("ActivateLocalProxy: Not using internal (NIC) sleep proxy for interface %s", ifname);
else
{
io_connect_t conObj;
mDNSlocal mDNSBool SystemSleepOnlyIfWakeOnLAN(void)
{
mDNSs32 val = 0;
- GetCurrentPMSetting(CFSTR("Idle Sleep Requires Network Proxy"), &val);
+ GetCurrentPMSetting(CFSTR("PrioritizeNetworkReachabilityOverSleep"), &val);
return val != 0 ? mDNStrue : mDNSfalse;
}
// Note: AutoTunnelRelayAddrIn is zeroed out in RemoveAutoTunnel6Record as it is called
// from other places.
m->AutoTunnelRelayAddrOut = zerov6Addr;
+ mDNS_Lock(m);
+ UpdateAutoTunnelDomainStatuses(m);
+ mDNS_Unlock(m);
return;
}
LogInfo("ParseBackToMyMac: non-NULL value for Interface, Adding autotunnel6");
AddAutoTunnel6Record(m, buf, BTMMDict);
}
+
+ mDNS_Lock(m);
+ UpdateAutoTunnelDomainStatuses(m);
+ mDNS_Unlock(m);
}
-mDNSexport void SetupConndConfigChanges(mDNS *const m)
+mDNSlocal void SetupConndConfigChanges(mDNS *const m)
{
CFDictionaryRef connd;
SCDynamicStoreRef store;
}
}
+// Called with KQueueLock & mDNS lock
+mDNSlocal void SetKeyChainTimer(mDNS *const m, mDNSs32 delay)
+ {
+ // If it's not set or it needs to happen sooner than when it's currently set
+ if (!m->p->KeyChainTimer || m->p->KeyChainTimer - NonZeroTime(m->timenow + delay) > 0)
+ {
+ m->p->KeyChainTimer = NonZeroTime(m->timenow + delay);
+ LogInfo("SetKeyChainTimer: %d", delay);
+ }
+ }
// Copy the fourth slash-delimited element from either:
// State:/Network/Interface/<bsdname>/IPv4
mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context)
{
(void)store; // Parameter not used
+ mDNSBool changeNow = mDNSfalse;
mDNS *const m = (mDNS *const)context;
KQueueLock(m);
mDNS_Lock(m);
int c4 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_DNS ) != 0);
if (c && c - c1 - c2 - c3 - c4 == 0) delay = mDNSPlatformOneSecond/10; // If these were the only changes, shorten delay
+ {
+ int i;
+ for (i=0; i<c; i++)
+ {
+ char buf[256];
+ if (!CFStringGetCString(CFArrayGetValueAtIndex(changedKeys, i), buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0;
+ if (buf[0])
+ {
+ if (strstr(buf, "p2p"))
+ {
+ LogInfo("NetworkChanged SC key: %s, not delaying network change", buf);
+ changeNow = mDNStrue;
+ break;
+ }
+ }
+ }
+ }
+
if (mDNS_LoggingEnabled)
{
int i;
delay);
}
+ mDNSBool btmmChanged = CFArrayContainsValue(changedKeys, range, NetworkChangedKey_BackToMyMac);
+ if (btmmChanged) delay = 0;
+
SetNetworkChanged(m, delay);
- // KeyChain frequently fails to notify clients of change events. To work around this
- // we set a timer and periodically poll to detect if any changes have occurred.
- // Without this Back To My Mac just does't work for a large number of users.
- // See <rdar://problem/5124399> Not getting Keychain Changed events when enabling BTMM
- if (c3 || CFArrayContainsValue(changedKeys, range, NetworkChangedKey_BackToMyMac))
- {
- LogInfo("*** NetworkChanged *** starting KeyChainBugTimer");
- m->p->KeyChainBugTimer = NonZeroTime(m->timenow + delay);
- m->p->KeyChainBugInterval = mDNSPlatformOneSecond;
- }
+ // Other software might pick up these changes to register or browse in WAB or BTMM domains,
+ // so in order for secure updates to be made to the server, make sure to read the keychain and
+ // setup the DomainAuthInfo before handing the network change.
+ // If we don't, then we will first try to register services in the clear, then later setup the
+ // DomainAuthInfo, which is incorrect.
+ if (c3 || btmmChanged)
+ SetKeyChainTimer(m, delay);
mDNS_Unlock(m);
// If DNS settings changed, immediately force a reconfig (esp. cache flush)
// Similarly, if an interface changed that is explicitly IPv4 link local, immediately force a reconfig
- if (c4 || ChangedKeysHaveIPv4LL(changedKeys)) mDNSMacOSXNetworkChanged(m);
+ if (c4 || ChangedKeysHaveIPv4LL(changedKeys) || changeNow) mDNSMacOSXNetworkChanged(m);
KQueueUnlock(m, "NetworkChanged");
}
+#if APPLE_OSX_mDNSResponder
+mDNSlocal void RefreshSPSStatus(const void *key, const void *value, void *context)
+ {
+ (void)context;
+ char buf[IFNAMSIZ];
+
+ CFStringRef ifnameStr = (CFStringRef)key;
+ CFArrayRef array = (CFArrayRef)value;
+ if (!CFStringGetCString(ifnameStr, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0;
+
+ LogInfo("RefreshSPSStatus: Updating SPS state for key %s, array count %d", buf, CFArrayGetCount(array));
+ mDNSDynamicStoreSetConfig(kmDNSSleepProxyServersState, buf, value);
+ }
+#endif
+
+mDNSlocal void DynamicStoreReconnected(SCDynamicStoreRef store, void *info)
+ {
+ mDNS *const m = (mDNS *const)info;
+ (void)store;
+
+ LogInfo("DynamicStoreReconnected: Reconnected");
+
+ // State:/Network/MulticastDNS
+ SetLocalDomains();
+
+ // State:/Network/DynamicDNS
+ if (m->FQDN.c[0])
+ mDNSPlatformDynDNSHostNameStatusChanged(&m->FQDN, 1);
+
+ // Note: PrivateDNS and BackToMyMac are automatically populated when configd is restarted
+ // as we receive network change notifications and thus not necessary. But we leave it here
+ // so that if things are done differently in the future, this code still works.
+
+ // State:/Network/PrivateDNS
+ CFMutableArrayRef sa = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+ if (!sa)
+ LogMsg("DynamicStoreReconnected:PrivateDNS: CFArrayCreateMutable failed");
+ else
+ {
+ DomainAuthInfo *FoundInList;
+ char stringbuf[MAX_ESCAPED_DOMAIN_NAME]; // Max legal domainname as C-string, including terminating NUL
+ for (FoundInList = m->AuthInfoList; FoundInList; FoundInList = FoundInList->next)
+ {
+ ConvertDomainNameToCString(&FoundInList->domain, stringbuf);
+ CFStringRef cfs = CFStringCreateWithCString(NULL, stringbuf, kCFStringEncodingUTF8);
+ if (cfs) { CFArrayAppendValue(sa, cfs); CFRelease(cfs); }
+ }
+ mDNSDynamicStoreSetConfig(kmDNSPrivateConfig, mDNSNULL, sa);
+ CFRelease(sa);
+ }
+
+ // State:/Network/BackToMyMac
+#if APPLE_OSX_mDNSResponder
+ mDNS_Lock(m);
+ UpdateAutoTunnelDomainStatuses(m);
+ mDNS_Unlock(m);
+
+ // State:/Network/Interface/en0/SleepProxyServers
+ if (spsStatusDict) CFDictionaryApplyFunction(spsStatusDict, RefreshSPSStatus, NULL);
+#endif
+ }
+
mDNSlocal mStatus WatchForNetworkChanges(mDNS *const m)
{
mStatus err = -1;
if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns))
{ LogMsg("SCDynamicStoreSetNotificationKeys failed: %s", SCErrorString(SCError())); goto error; }
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
if (!SCDynamicStoreSetDispatchQueue(store, dispatch_get_main_queue()))
{ LogMsg("SCDynamicStoreCreateRunLoopSource failed: %s", SCErrorString(SCError())); goto error; }
#else
if (!m->p->StoreRLS) { LogMsg("SCDynamicStoreCreateRunLoopSource failed: %s", SCErrorString(SCError())); goto error; }
CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode);
#endif
+ SCDynamicStoreSetDisconnectCallBack(store, DynamicStoreReconnected);
m->p->Store = store;
err = 0;
goto exit;
#define KEV_DL_WAKEFLAGS_CHANGED 17
#endif
+#if !TARGET_OS_EMBEDDED // don't setup packet filter rules on embedded
+
+mDNSlocal void mDNSSetPacketFilterRules(mDNS *const m, char * ifname)
+ {
+ AuthRecord *rr;
+ int found = 0;
+
+ for (rr = m->ResourceRecords; rr; rr=rr->next)
+ {
+ if ((rr->resrec.rrtype == kDNSServiceType_SRV) && (rr->ARType == AuthRecordAnyIncludeP2P))
+ {
+ uint16_t port = rr->resrec.rdata->u.srv.port.NotAnInteger;
+ uint16_t protocol;
+ const mDNSu8 *p;
+
+ LogInfo("mDNSSetPacketFilterRules: found %s", ARDisplayString(m, rr));
+
+ // Note presence of more than one p2p service in list
+ // since code is currently opening only one port in the packet filter.
+ found++;
+ if (found > 1)
+ {
+ LogMsg("mDNSSetPacketFilterRules: found service #%d %s", found, ARDisplayString(m, rr));
+ }
+
+ // Assume <Service Instance>.<App Protocol>.<Transport Protocol>.<Name>
+ p = rr->resrec.name->c;
+
+ // Skip to App Protocol
+ if (p[0]) p += 1 + p[0];
+ // Skip to Transport Protocol
+ if (p[0]) p += 1 + p[0];
+
+ if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_tcp")) protocol = IPPROTO_TCP;
+ else if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_udp")) protocol = IPPROTO_UDP;
+ else
+ {
+ LogMsg("mDNSSetPacketFilterRules: could not determine transport protocol of service");
+ LogMsg("mDNSSetPacketFilterRules: %s", ARDisplayString(m, rr));
+ return;
+ }
+
+ LogInfo("mDNSSetPacketFilterRules: ifname %s, port(in NBO)0x%X, protocol %d",
+ ifname, port, protocol);
+ mDNSPacketFilterControl(PF_SET_RULES, ifname, port, protocol);
+ }
+ }
+ }
+#endif // !TARGET_OS_EMBEDDED
+
+
+#if APPLE_OSX_mDNSResponder
+
+#if !TARGET_OS_EMBEDDED
+// If the p2p interface already exists, set the Bonjour packet filter rules for it.
+mDNSexport void mDNSInitPacketFilter(void)
+ {
+ mDNS *const m = & mDNSStorage;
+
+ NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces);
+ while (intf)
+ {
+ if (strncmp(intf->ifname, "p2p", 3) == 0)
+ {
+ LogInfo("mDNSInitPacketFilter: Setting rules for ifname %s", intf->ifname);
+ mDNSSetPacketFilterRules(m, intf->ifname);
+ break;
+ }
+ intf = GetFirstActiveInterface(intf->next);
+ }
+ }
+
+#else // !TARGET_OS_EMBEDDED
+
+ // Currently no packet filter setup required embedded on platforms.
+mDNSexport void mDNSInitPacketFilter()
+ {
+ }
+
+#endif // !TARGET_OS_EMBEDDED
+#endif // APPLE_OSX_mDNSResponder
+
mDNSlocal void SysEventCallBack(int s1, short __unused filter, void *context)
{
mDNS *const m = (mDNS *const)context;
if (msg.k.event_code == KEV_DL_WAKEFLAGS_CHANGED || msg.k.event_code == KEV_DL_LINK_ON)
SetNetworkChanged(m, mDNSPlatformOneSecond * 2);
+
+#if !TARGET_OS_EMBEDDED // don't setup packet filter rules on embedded
+
+ // For p2p interfaces, need to open the advertised service port in the firewall.
+ if (msg.k.event_code == KEV_DL_IF_ATTACHED)
+ {
+ struct net_event_data * p;
+ p = (struct net_event_data *) & msg.k.event_data;
+
+ if (strncmp(p->if_name, "p2p", 3) == 0)
+ {
+ char ifname[IFNAMSIZ];
+ snprintf(ifname, IFNAMSIZ, "%s%d", p->if_name, p->if_unit);
+
+ LogInfo("SysEventCallBack: KEV_DL_IF_ATTACHED if_family = %d, if_unit = %d, if_name = %s", p->if_family, p->if_unit, p->if_name);
+
+ mDNSSetPacketFilterRules(m, ifname);
+ }
+ }
+
+ // For p2p interfaces, need to clear the firewall rules on interface detach
+ if (msg.k.event_code == KEV_DL_IF_DETACHED)
+ {
+ struct net_event_data * p;
+ p = (struct net_event_data *) & msg.k.event_data;
+
+ if (strncmp(p->if_name, "p2p", 3) == 0)
+ {
+ char ifname[IFNAMSIZ];
+ snprintf(ifname, IFNAMSIZ, "%s%d", p->if_name, p->if_unit);
+
+ LogInfo("SysEventCallBack: KEV_DL_IF_DETACHED if_family = %d, if_unit = %d, if_name = %s", p->if_family, p->if_unit, p->if_name);
+
+ mDNSPacketFilterControl(PF_CLEAR_RULES, ifname, 0, 0);
+ }
+ }
+#endif // !TARGET_OS_EMBEDDED
+
}
mDNS_Unlock(m);
if (!err)
{
relevant = ((a->attr[0].length == 4 && (!strncasecmp(a->attr[0].data, "ddns", 4) || !strncasecmp(a->attr[0].data, "sndd", 4))) ||
- (a->attr[1].length >= 4 && (!strncasecmp(a->attr[1].data, "dns:", 4))));
+ (a->attr[1].length >= mDNSPlatformStrLen(dnsprefix) && (!strncasecmp(a->attr[1].data, dnsprefix, mDNSPlatformStrLen(dnsprefix)))) ||
+ (a->attr[1].length >= mDNSPlatformStrLen(btmmprefix) && (!strncasecmp(a->attr[1].data, btmmprefix, mDNSPlatformStrLen(btmmprefix)))));
SecKeychainItemFreeAttributesAndData(a, NULL);
}
}
// We're running on the CFRunLoop (Mach port) thread, not the kqueue thread, so we need to grab the KQueueLock before proceeding
KQueueLock(m);
mDNS_Lock(m);
- SetDomainSecrets(m);
+
+ // To not read the keychain twice: when BTMM is enabled, changes happen to the keychain
+ // then the BTMM DynStore dictionary, so delay reading the keychain for a second.
+ // NetworkChanged() will reset the keychain timer to fire immediately when the DynStore changes.
+ //
+ // In the "fixup" case where the BTMM DNS servers aren't accepting the key mDNSResponder has,
+ // the DynStore dictionary won't change (because the BTMM zone won't change). In that case,
+ // a one second delay is ok, as we'll still converge to correctness, and there's no race
+ // condition between the RegistrationDomain and the DomainAuthInfo.
+ //
+ // Lastly, non-BTMM WAB cases can use the keychain but not the DynStore, so we need to set
+ // the timer here, as it will not get set by NetworkChanged().
+ SetKeyChainTimer(m, mDNSPlatformOneSecond);
+
mDNS_Unlock(m);
KQueueUnlock(m, "KeychainChanged");
}
{
long utc = mDNSPlatformUTC();
mDNSPowerRequest(-1,-1); // Need to explicitly clear any previous power requests -- they're not cleared automatically on wake
- if (m->p->WakeAtUTC - utc > 30) LogInfo("PowerChanged PowerOn %d seconds early, assuming not maintenance wake", m->p->WakeAtUTC - utc);
- else if (utc - m->p->WakeAtUTC > 30) LogInfo("PowerChanged PowerOn %d seconds late, assuming not maintenance wake", utc - m->p->WakeAtUTC);
+ if (m->p->WakeAtUTC - utc > 30) LogSPS("PowerChanged PowerOn %d seconds early, assuming not maintenance wake", m->p->WakeAtUTC - utc);
+ else if (utc - m->p->WakeAtUTC > 30) LogSPS("PowerChanged PowerOn %d seconds late, assuming not maintenance wake", utc - m->p->WakeAtUTC);
+ else if (!strncasecmp(HINFO_HWstring, "K66AP", 5)) LogSPS("PowerChanged PowerOn %d seconds late, device is K66AP so not re-sleeping", utc - m->p->WakeAtUTC);
else
{
- mDNSs32 i = 20;
- //int result = mDNSPowerRequest(0, i);
- //if (result == kIOReturnNotReady) do i += (i<20) ? 1 : ((i+3)/4); while (mDNSPowerRequest(0, i) == kIOReturnNotReady);
- LogMsg("PowerChanged: Waking for network maintenance operations %d seconds early; re-sleeping in %d seconds", m->p->WakeAtUTC - utc, i);
- m->p->RequestReSleep = mDNS_TimeNow(m) + i * mDNSPlatformOneSecond;
+ LogSPS("PowerChanged: Waking for network maintenance operations %d seconds early; re-sleeping in 20 seconds", m->p->WakeAtUTC - utc);
+ m->p->RequestReSleep = mDNS_TimeNow(m) + 20 * mDNSPlatformOneSecond;
}
}
}
switch(messageType)
{
- case kIOMessageCanSystemPowerOff: LogInfo("PowerChanged kIOMessageCanSystemPowerOff (no action)"); break; // E0000240
- case kIOMessageSystemWillPowerOff: LogInfo("PowerChanged kIOMessageSystemWillPowerOff"); // E0000250
+ case kIOMessageCanSystemPowerOff: LogSPS("PowerChanged kIOMessageCanSystemPowerOff (no action)"); break; // E0000240
+ case kIOMessageSystemWillPowerOff: LogSPS("PowerChanged kIOMessageSystemWillPowerOff"); // E0000250
mDNSCoreMachineSleep(m, true);
if (m->SleepState == SleepState_Sleeping) mDNSMacOSXNetworkChanged(m);
break;
- case kIOMessageSystemWillNotPowerOff: LogInfo("PowerChanged kIOMessageSystemWillNotPowerOff (no action)"); break; // E0000260
- case kIOMessageCanSystemSleep: LogInfo("PowerChanged kIOMessageCanSystemSleep (no action)"); break; // E0000270
- case kIOMessageSystemWillSleep: LogInfo("PowerChanged kIOMessageSystemWillSleep"); // E0000280
+ case kIOMessageSystemWillNotPowerOff: LogSPS("PowerChanged kIOMessageSystemWillNotPowerOff (no action)"); break; // E0000260
+ case kIOMessageCanSystemSleep: LogSPS("PowerChanged kIOMessageCanSystemSleep (no action)"); break; // E0000270
+ case kIOMessageSystemWillSleep: LogSPS("PowerChanged kIOMessageSystemWillSleep"); // E0000280
mDNSCoreMachineSleep(m, true);
break;
- case kIOMessageSystemWillNotSleep: LogInfo("PowerChanged kIOMessageSystemWillNotSleep (no action)"); break; // E0000290
- case kIOMessageSystemHasPoweredOn: LogInfo("PowerChanged kIOMessageSystemHasPoweredOn"); // E0000300
+ case kIOMessageSystemWillNotSleep: LogSPS("PowerChanged kIOMessageSystemWillNotSleep (no action)"); break; // E0000290
+ case kIOMessageSystemHasPoweredOn: LogSPS("PowerChanged kIOMessageSystemHasPoweredOn"); // E0000300
// If still sleeping (didn't get 'WillPowerOn' message for some reason?) wake now
if (m->SleepState)
{
mDNS_Unlock(m);
break;
- case kIOMessageSystemWillRestart: LogInfo("PowerChanged kIOMessageSystemWillRestart (no action)"); break; // E0000310
- case kIOMessageSystemWillPowerOn: LogInfo("PowerChanged kIOMessageSystemWillPowerOn"); // E0000320
+ case kIOMessageSystemWillRestart: LogSPS("PowerChanged kIOMessageSystemWillRestart (no action)"); break; // E0000310
+ case kIOMessageSystemWillPowerOn: LogSPS("PowerChanged kIOMessageSystemWillPowerOn"); // E0000320
- #if ! TARGET_OS_EMBEDDED
// On Leopard and earlier, on wake from sleep, instead of reporting link state down, Apple
// Ethernet drivers report "hardware incapable of detecting link state", which the kernel
// interprets as "should assume we have networking", which results in the first 4-5 seconds
// of packets vanishing into a black hole. To work around this, on wake from sleep,
// we block for five seconds to let Ethernet come up, and then resume normal operation.
- if (OSXVers < OSXVers_10_6_SnowLeopard)
+ if (OSXVers && OSXVers < OSXVers_10_6_SnowLeopard)
{
sleep(5);
LogMsg("Running on Mac OS X version 10.%d earlier than 10.6; "
"PowerChanged did sleep(5) to wait for Ethernet hardware", OSXVers - OSXVers_Base);
}
- #endif
// Make sure our interface list is cleared to the empty state, then tell mDNSCore to wake
if (m->SleepState != SleepState_Sleeping)
}
PowerOn(m);
break;
- default: LogInfo("PowerChanged unknown message %X", messageType); break;
+ default: LogSPS("PowerChanged unknown message %X", messageType); break;
}
if (messageType == kIOMessageSystemWillSleep) m->p->SleepCookie = (long)messageArgument;
}
#endif
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - /etc/hosts support
+#endif
+
+// Implementation Notes
+//
+// As /etc/hosts file can be huge (1000s of entries - when this comment was written, the test file had about
+// 23000 entries with about 4000 duplicates), we can't use a linked list to store these entries. So, we parse
+// them into a hash table. The implementation need to be able to do the following things efficiently
+//
+// 1. Detect duplicates e.g., two entries with "1.2.3.4 foo"
+// 2. Detect whether /etc/hosts has changed and what has changed since the last read from the disk
+// 3. Ability to support multiple addresses per name e.g., "1.2.3.4 foo, 2.3.4.5 foo". To support this, we
+// need to be able set the RRSet of a resource record to the first one in the list and also update when
+// one of them go away. This is needed so that the core thinks that they are all part of the same RRSet and
+// not a duplicate
+// 4. Don't maintain any local state about any records registered with the core to detect changes to /etc/hosts
+//
+// CFDictionary is not a suitable candidate because it does not support duplicates and even if we use a custom
+// "hash" function to solve this, the others are hard to solve. Hence, we share the hash (AuthHash) implementation
+// of the core layer which does all of the above very efficiently
+
+mDNSexport void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result)
+ {
+ (void)m; // unused
+ (void)rr;
+ (void)result;
+ if (result == mStatus_MemFree)
+ {
+ LogInfo("FreeEtcHosts: %s", ARDisplayString(m, rr));
+ freeL("etchosts", rr);
+ }
+ }
+
+// Returns true on success and false on failure
+mDNSlocal mDNSBool mDNSMacOSXCreateEtcHostsEntry(mDNS *const m, const domainname *domain, const struct sockaddr *sa, const domainname *cname, char *ifname, AuthHash *auth)
+ {
+ AuthRecord *rr;
+ mDNSu32 slot;
+ mDNSu32 namehash;
+ AuthGroup *ag;
+ mDNSInterfaceID InterfaceID = mDNSInterface_LocalOnly;
+ mDNSu16 rrtype;
+
+ if (!domain)
+ {
+ LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! name NULL");
+ return mDNSfalse;
+ }
+ if (!sa && !cname)
+ {
+ LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! sa and cname both NULL");
+ return mDNSfalse;
+ }
+
+ if (sa && sa->sa_family != AF_INET && sa->sa_family != AF_INET6)
+ {
+ LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! sa with bad family %d", sa->sa_family);
+ return mDNSfalse;
+ }
+
+
+ if (ifname)
+ {
+ mDNSu32 ifindex = if_nametoindex(ifname);
+ if (!ifindex)
+ {
+ LogMsg("mDNSMacOSXCreateEtcHostsEntry: hosts entry %##s with invalid ifname %s", domain->c, ifname);
+ return mDNSfalse;
+ }
+ InterfaceID = (mDNSInterfaceID)(uintptr_t)ifindex;
+ }
+
+ if (sa)
+ rrtype = (sa->sa_family == AF_INET ? kDNSType_A : kDNSType_AAAA);
+ else
+ rrtype = kDNSType_CNAME;
+
+ // Check for duplicates. See whether we parsed an entry before like this ?
+ slot = AuthHashSlot(domain);
+ namehash = DomainNameHashValue(domain);
+ ag = AuthGroupForName(auth, slot, namehash, domain);
+ if (ag)
+ {
+ rr = ag->members;
+ while (rr)
+ {
+ if (rr->resrec.rrtype == rrtype)
+ {
+ if (rrtype == kDNSType_A)
+ {
+ mDNSv4Addr ip;
+ ip.NotAnInteger = ((struct sockaddr_in*)sa)->sin_addr.s_addr;
+ if (mDNSSameIPv4Address(rr->resrec.rdata->u.ipv4, ip))
+ {
+ LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same IPv4 address for name %##s", domain->c);
+ return mDNSfalse;
+ }
+ }
+ else if (rrtype == kDNSType_AAAA)
+ {
+ mDNSv6Addr ip6;
+ ip6.l[0] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[0];
+ ip6.l[1] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[1];
+ ip6.l[2] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[2];
+ ip6.l[3] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[3];
+ if (mDNSSameIPv6Address(rr->resrec.rdata->u.ipv6, ip6))
+ {
+ LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same IPv6 address for name %##s", domain->c);
+ return mDNSfalse;
+ }
+ }
+ else if (rrtype == kDNSType_CNAME)
+ {
+ if (SameDomainName(&rr->resrec.rdata->u.name, cname))
+ {
+ LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same cname %##s for name %##s", cname->c, domain->c);
+ return mDNSfalse;
+ }
+ }
+ }
+ rr = rr->next;
+ }
+ }
+ rr= mallocL("etchosts", sizeof(*rr));
+ if (rr == NULL) return mDNSfalse;
+ mDNSPlatformMemZero(rr, sizeof(*rr));
+ mDNS_SetupResourceRecord(rr, NULL, InterfaceID, rrtype, 1, kDNSRecordTypeKnownUnique, AuthRecordLocalOnly, FreeEtcHosts, NULL);
+ AssignDomainName(&rr->namestorage, domain);
+
+ if (sa)
+ {
+ rr->resrec.rdlength = sa->sa_family == AF_INET ? sizeof(mDNSv4Addr) : sizeof(mDNSv6Addr);
+ if (sa->sa_family == AF_INET)
+ rr->resrec.rdata->u.ipv4.NotAnInteger = ((struct sockaddr_in*)sa)->sin_addr.s_addr;
+ else
+ {
+ rr->resrec.rdata->u.ipv6.l[0] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[0];
+ rr->resrec.rdata->u.ipv6.l[1] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[1];
+ rr->resrec.rdata->u.ipv6.l[2] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[2];
+ rr->resrec.rdata->u.ipv6.l[3] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[3];
+ }
+ }
+ else
+ {
+ rr->resrec.rdlength = DomainNameLength(cname);
+ rr->resrec.rdata->u.name.c[0] = 0;
+ AssignDomainName(&rr->resrec.rdata->u.name, cname);
+ }
+ rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
+ SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rr->rdatahash for us
+ LogInfo("mDNSMacOSXCreateEtcHostsEntry: Adding resource record %s", ARDisplayString(m, rr));
+ InsertAuthRecord(m, auth, rr);
+ return mDNStrue;
+ }
+
+mDNSlocal int EtcHostsParseOneName(int start, int length, char *buffer, char **name)
+ {
+ int i;
+
+ *name = NULL;
+ for (i = start; i < length; i++)
+ {
+ if (buffer[i] != ' ' && buffer[i] != ',' && buffer[i] != '\t')
+ {
+ *name = &buffer[i];
+
+ // Found the start of a name, find the end and null terminate
+ for (i++; i < length; i++)
+ {
+ if (buffer[i] == ' ' || buffer[i] == ',' || buffer[i] == '\t')
+ {
+ buffer[i] = 0;
+ break;
+ }
+ }
+ return i;
+ }
+ }
+ return -1;
+ }
+
+mDNSlocal void mDNSMacOSXParseEtcHostsLine(mDNS *const m, char *buffer, ssize_t length, AuthHash *auth)
+ {
+ int i;
+ int ifStart = 0;
+ char *ifname = NULL;
+ domainname name1d;
+ domainname name2d;
+ char *name1;
+ char *name2;
+ int aliasIndex;
+
+ // Find the end of the address string
+ for (i = 0; i < length; i++)
+ {
+ if (buffer[i] == ' ' || buffer[i] == ',' || buffer[i] == '\t' || buffer[i] == '%')
+ {
+ if (buffer[i] == '%')
+ ifStart = i + 1;
+ buffer[i] = 0;
+ break;
+ }
+ }
+
+ // Convert the address string to an address
+ struct addrinfo hints;
+ bzero(&hints, sizeof(hints));
+ hints.ai_flags = AI_NUMERICHOST;
+ struct addrinfo *gairesults = NULL;
+ if (getaddrinfo(buffer, NULL, &hints, &gairesults) != 0)
+ {
+ LogInfo("mDNSMacOSXParseEtcHostsLine: getaddrinfo returning null");
+ return;
+ }
+
+ if (ifStart)
+ {
+ // Parse the interface
+ ifname = &buffer[ifStart];
+ for (i = ifStart + 1; i < length; i++)
+ {
+ if (buffer[i] == ' ' || buffer[i] == ',' || buffer[i] == '\t')
+ {
+ buffer[i] = 0;
+ break;
+ }
+ }
+ }
+
+ i = EtcHostsParseOneName(i + 1, length, buffer, &name1);
+ if (i == length)
+ {
+ // Common case (no aliases) : The entry is of the form "1.2.3.4 somehost" with no trailing white spaces/tabs etc.
+ if (!MakeDomainNameFromDNSNameString(&name1d, name1))
+ {
+ LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name1);
+ freeaddrinfo(gairesults);
+ return;
+ }
+ mDNSMacOSXCreateEtcHostsEntry(m, &name1d, gairesults->ai_addr, mDNSNULL, ifname, auth);
+ }
+ else if (i != -1)
+ {
+ domainname first;
+ // We might have some extra white spaces at the end for the common case of "1.2.3.4 somehost".
+ // When we parse again below, EtchHostsParseOneName would return -1 and we will end up
+ // doing the right thing.
+ if (!MakeDomainNameFromDNSNameString(&first, name1))
+ {
+ LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name1);
+ freeaddrinfo(gairesults);
+ return;
+ }
+ // If the /etc/hosts has an entry like this
+ //
+ // 1.2.3.4 sun star bright
+ //
+ // star and bright are aliases (gethostbyname h_alias should point to these) and sun is the canonical
+ // name (getaddrinfo ai_cannonname and gethostbyname h_name points to "sun")
+ //
+ // To achieve this, we need to add the entry like this:
+ //
+ // star CNAME bright
+ // bright CNAME sun
+ // sun A 1.2.3.4
+ //
+ // We store the first name we parsed in "first". Then we parse additional names adding CNAME records
+ // till we reach the end. When we reach the end, we wrap around and add one final CNAME with the last
+ // entry and the first entry. Finally, we add the Address (A/AAAA) record.
+ aliasIndex = 0;
+ while (i <= length)
+ {
+ // Parse a name. If there are no names, we need to know whether we
+ // parsed CNAMEs before or not. If we parsed CNAMEs before, then we
+ // add a CNAME with the last name and the first name. Otherwise, this
+ // is same as the common case above where the line has just one name
+ // but with trailing white spaces.
+ i = EtcHostsParseOneName(i + 1, length, buffer, &name2);
+ if (name2)
+ {
+ if (!MakeDomainNameFromDNSNameString(&name2d, name2))
+ {
+ LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name2);
+ freeaddrinfo(gairesults);
+ return;
+ }
+ aliasIndex++;
+ }
+ else if (!aliasIndex)
+ {
+ // We have never parsed any aliases. This case happens if there
+ // is just one name and some extra white spaces at the end.
+ LogInfo("mDNSMacOSXParseEtcHostsLine: White space at the end of %##s", first.c);
+ break;
+ }
+ else
+ {
+ // We have parsed at least one alias before and we reached the end of the line.
+ // Setup a CNAME for the last name with "first" name as its RDATA
+ name2d.c[0] = 0;
+ AssignDomainName(&name2d, &first);
+ }
+
+ // Don't add a CNAME for the first alias we parse (see the example above).
+ // As we parse more, we might discover that there are no more aliases, in
+ // which case we would have set "name2d" to "first" above. We need to add
+ // the CNAME in that case.
+
+ if (aliasIndex > 1 || SameDomainName(&name2d, &first))
+ {
+ // Ignore if it points to itself
+ if (!SameDomainName(&name1d, &name2d))
+ {
+ if (!mDNSMacOSXCreateEtcHostsEntry(m, &name1d, mDNSNULL, &name2d, ifname, auth))
+ {
+ freeaddrinfo(gairesults);
+ return;
+ }
+ }
+ else
+ LogMsg("mDNSMacOSXParseEtcHostsLine: Ignoring entry with same names name1 %##s, name2 %##s", name1d.c, name2d.c);
+ }
+
+ // If we have already wrapped around, we just need to add the A/AAAA record alone
+ // which is done below
+ if (SameDomainName(&name2d, &first)) break;
+
+ // Remember the current name so that we can set the CNAME record if we parse one
+ // more name
+ name1d.c[0] = 0;
+ AssignDomainName(&name1d, &name2d);
+ }
+ // Added all the CNAMEs if any, add the "A/AAAA" record
+ mDNSMacOSXCreateEtcHostsEntry(m, &first, gairesults->ai_addr, mDNSNULL, ifname, auth);
+ }
+ freeaddrinfo(gairesults);
+ }
+
+mDNSlocal void mDNSMacOSXParseEtcHosts(mDNS *const m, int fd, AuthHash *auth)
+ {
+ if (fd == -1) { LogInfo("mDNSMacOSXParseEtcHosts: fd is -1"); return; }
+
+ LogInfo("mDNSMacOSXParseEtcHosts: Parsing etc hosts");
+
+ // Read through the file
+ char readBuf[1024]; // support a maximum line of 1024 bytes including newline
+ off_t offset = 0;
+ int comment = 0;
+ ssize_t i = 0;
+ ssize_t length;
+ ssize_t linestart;
+ while ((length = pread(fd, &readBuf[i], sizeof(readBuf) - i, offset) + i) >= i)
+ {
+ if (length == i)
+ {
+ // end of file
+ if (length != 0) mDNSMacOSXParseEtcHostsLine(m, readBuf, length, auth);
+ break;
+ }
+
+ // increment our offset by the number of bytes we read
+ offset += length - i;
+
+ // loop through the buffer looking for end of lines
+ for (linestart = 0; i < length; i++)
+ {
+ if (readBuf[i] == '#' || readBuf[i] == '\r' || readBuf[i] == '\n')
+ {
+ int parseline = i - linestart > 0 && comment == 0;
+ comment = readBuf[i] == '#';
+ if (parseline)
+ {
+ readBuf[i] = 0;
+ mDNSMacOSXParseEtcHostsLine(m, &readBuf[linestart], i - linestart, auth);
+ }
+ linestart = i + 1;
+ }
+ }
+
+ // prepare to read the next chunk of the file
+ if (linestart == 0)
+ {
+ // single line was larger than our buffer, we're going to ignore it
+ comment = 1; // treat remainder of line as comment
+ i = 0;
+ }
+ else
+ {
+ i = length - linestart;
+ if (i) memmove(readBuf, &readBuf[linestart], i);
+ }
+ }
+ }
+
+mDNSlocal void mDNSMacOSXUpdateEtcHosts(mDNS *const m);
+
+mDNSlocal int mDNSMacOSXGetEtcHostsFD(mDNS *const m)
+ {
+#ifdef __DISPATCH_GROUP__
+ // Can't do this stuff to be notified of changes in /etc/hosts if we don't have libdispatch
+ static dispatch_queue_t etcq = 0;
+ static dispatch_source_t etcsrc = 0;
+ static dispatch_source_t hostssrc = 0;
+
+ // First time through? just schedule ourselves on the main queue and we'll do the work later
+ if (!etcq)
+ {
+ etcq = dispatch_get_main_queue();
+ if (etcq)
+ {
+ // Do this work on the queue, not here - solves potential synchronization issues
+ dispatch_async(etcq, ^{mDNSMacOSXUpdateEtcHosts(m);});
+ }
+ return -1;
+ }
+
+ if (hostssrc) return dispatch_source_get_handle(hostssrc);
+#endif
+
+ int fd = open("/etc/hosts", O_RDONLY);
+
+#ifdef __DISPATCH_GROUP__
+ // Can't do this stuff to be notified of changes in /etc/hosts if we don't have libdispatch
+ if (fd == -1)
+ {
+ // If the open failed and we're already watching /etc, we're done
+ if (etcsrc) { LogInfo("mDNSMacOSXGetEtcHostsFD: Returning etcfd because no etchosts"); return fd; }
+
+ // we aren't watching /etc, we should be
+ fd = open("/etc", O_RDONLY);
+ if (fd == -1) { LogInfo("mDNSMacOSXGetEtcHostsFD: etc does not exist"); return -1; }
+ etcsrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_RENAME, etcq);
+ if (etcsrc == NULL)
+ {
+ close(fd);
+ return -1;
+ }
+ dispatch_source_set_event_handler(etcsrc,
+ ^{
+ u_int32_t flags = dispatch_source_get_data(etcsrc);
+ LogMsg("mDNSMacOSXGetEtcHostsFD: /etc changed 0x%x", flags);
+ if ((flags & (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_RENAME)) != 0)
+ {
+ dispatch_source_cancel(etcsrc);
+ dispatch_release(etcsrc);
+ etcsrc = NULL;
+ dispatch_async(etcq, ^{mDNSMacOSXUpdateEtcHosts(m);});
+ return;
+ }
+ if ((flags & DISPATCH_VNODE_WRITE) != 0 && hostssrc == NULL)
+ {
+ mDNSMacOSXUpdateEtcHosts(m);
+ }
+ });
+ dispatch_source_set_cancel_handler(etcsrc, ^{close(fd);});
+ dispatch_resume(etcsrc);
+
+ // Try and open /etc/hosts once more now that we're watching /etc, in case we missed the creation
+ fd = open("/etc/hosts", O_RDONLY | O_EVTONLY);
+ if (fd == -1) { LogMsg("mDNSMacOSXGetEtcHostsFD etc hosts does not exist, watching etc"); return -1; }
+ }
+
+ // create a dispatch source to watch for changes to hosts file
+ hostssrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd,
+ (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_RENAME |
+ DISPATCH_VNODE_ATTRIB | DISPATCH_VNODE_EXTEND | DISPATCH_VNODE_LINK | DISPATCH_VNODE_REVOKE), etcq);
+ if (hostssrc == NULL)
+ {
+ close(fd);
+ return -1;
+ }
+ dispatch_source_set_event_handler(hostssrc,
+ ^{
+ u_int32_t flags = dispatch_source_get_data(hostssrc);
+ LogInfo("mDNSMacOSXGetEtcHostsFD: /etc/hosts changed 0x%x", flags);
+ if ((flags & (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_RENAME)) != 0)
+ {
+ dispatch_source_cancel(hostssrc);
+ dispatch_release(hostssrc);
+ hostssrc = NULL;
+ // Bug in LibDispatch: wait a second before scheduling the block. If we schedule
+ // the block immediately, we try to open the file and the file may not exist and may
+ // fail to get a notification in the future. When the file does not exist and
+ // we start to monitor the directory, on "dispatch_resume" of that source, there
+ // is no guarantee that the file creation will be notified always because when
+ // the dispatch_resume returns, the kevent manager may not have registered the
+ // kevent yet but the file may have been created
+ usleep(1000000);
+ dispatch_async(etcq, ^{mDNSMacOSXUpdateEtcHosts(m);});
+ return;
+ }
+ if ((flags & DISPATCH_VNODE_WRITE) != 0)
+ {
+ mDNSMacOSXUpdateEtcHosts(m);
+ }
+ });
+ dispatch_source_set_cancel_handler(hostssrc, ^{LogInfo("mDNSMacOSXGetEtcHostsFD: Closing etchosts fd %d", fd); close(fd);});
+ dispatch_resume(hostssrc);
+
+ // Cleanup /etc source, no need to watch it if we already have /etc/hosts
+ if (etcsrc)
+ {
+ dispatch_source_cancel(etcsrc);
+ dispatch_release(etcsrc);
+ etcsrc = NULL;
+ }
+
+ LogInfo("mDNSMacOSXGetEtcHostsFD: /etc/hosts being monitored, and not etc");
+ return hostssrc ? (int)dispatch_source_get_handle(hostssrc) : -1;
+#else
+ (void)m;
+ return fd;
+#endif;
+ }
+
+// When /etc/hosts is modified, flush all the cache records as there may be local
+// authoritative answers now
+mDNSlocal void FlushAllCacheRecords(mDNS *const m)
+ {
+ CacheRecord *cr;
+ mDNSu32 slot;
+ CacheGroup *cg;
+
+ FORALL_CACHERECORDS(slot, cg, cr)
+ {
+ // Skip multicast.
+ if (cr->resrec.InterfaceID) continue;
+
+ // If a resource record can answer A or AAAA, they need to be flushed so that we will
+ // never used to deliver an ADD or RMV
+ if (RRTypeAnswersQuestionType(&cr->resrec, kDNSType_A) ||
+ RRTypeAnswersQuestionType(&cr->resrec, kDNSType_AAAA))
+ {
+ LogInfo("FlushAllCacheRecords: Purging Resourcerecord %s", CRDisplayString(m, cr));
+ mDNS_PurgeCacheResourceRecord(m, cr);
+ }
+ }
+ }
+
+// Add new entries to the core. If justCheck is set, this function does not add, just returns true
+mDNSlocal mDNSBool EtcHostsAddNewEntries(mDNS *const m, AuthHash *newhosts, mDNSBool justCheck)
+ {
+ AuthGroup *ag;
+ mDNSu32 slot;
+ AuthRecord *rr, *primary, *rrnext;
+ for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+ for (ag = newhosts->rrauth_hash[slot]; ag; ag = ag->next)
+ {
+ primary = NULL;
+ for (rr = ag->members; rr; rr = rrnext)
+ {
+ rrnext = rr->next;
+ AuthGroup *ag1;
+ AuthRecord *rr1;
+ mDNSBool found = mDNSfalse;
+ ag1 = AuthGroupForRecord(&m->rrauth, slot, &rr->resrec);
+ if (ag1 && ag1->members)
+ {
+ if (!primary) primary = ag1->members;
+ rr1 = ag1->members;
+ while (rr1)
+ {
+ // We are not using InterfaceID in checking for duplicates. This means,
+ // if there are two addresses for a given name e.g., fe80::1%en0 and
+ // fe80::1%en1, we only add the first one. It is not clear whether
+ // this is a common case. To fix this, we also need to modify
+ // mDNS_Register_internal in how it handles duplicates. If it becomes a
+ // common case, we will fix it then.
+ if (IdenticalResourceRecord(&rr1->resrec, &rr->resrec))
+ {
+ LogInfo("EtcHostsAddNewEntries: Skipping, not adding %s", ARDisplayString(m, rr1));
+ found = mDNStrue;
+ break;
+ }
+ rr1 = rr1->next;
+ }
+ }
+ if (!found)
+ {
+ if (justCheck)
+ {
+ LogInfo("EtcHostsAddNewEntries: Entry %s not registered with core yet", ARDisplayString(m, rr));
+ return mDNStrue;
+ }
+ RemoveAuthRecord(m, newhosts, rr);
+ // if there is no primary, point to self
+ rr->RRSet = (primary ? primary : rr);
+ rr->next = NULL;
+ LogInfo("EtcHostsAddNewEntries: Adding %s", ARDisplayString(m, rr));
+ if (mDNS_Register_internal(m, rr) != mStatus_NoError)
+ LogMsg("EtcHostsAddNewEntries: mDNS_Register failed for %s", ARDisplayString(m, rr));
+ }
+ }
+ }
+ return mDNSfalse;
+ }
+
+// Delete entries from the core that are no longer needed. If justCheck is set, this function
+// does not delete, just returns true
+mDNSlocal mDNSBool EtcHostsDeleteOldEntries(mDNS *const m, AuthHash *newhosts, mDNSBool justCheck)
+ {
+ AuthGroup *ag;
+ mDNSu32 slot;
+ AuthRecord *rr, *primary, *rrnext;
+ for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+ for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next)
+ for (rr = ag->members; rr; rr = rrnext)
+ {
+ mDNSBool found = mDNSfalse;
+ AuthGroup *ag1;
+ AuthRecord *rr1;
+ rrnext = rr->next;
+ if (rr->RecordCallback != FreeEtcHosts) continue;
+ ag1 = AuthGroupForRecord(newhosts, slot, &rr->resrec);
+ if (ag1)
+ {
+ primary = rr1 = ag1->members;
+ while (rr1)
+ {
+ if (IdenticalResourceRecord(&rr1->resrec, &rr->resrec))
+ {
+ LogInfo("EtcHostsDeleteOldEntries: Old record %s found in new, skipping", ARDisplayString(m, rr));
+ found = mDNStrue;
+ break;
+ }
+ rr1 = rr1->next;
+ }
+ }
+ // there is no corresponding record in newhosts for the same name. This means
+ // we should delete this from the core.
+ if (!found)
+ {
+ if (justCheck)
+ {
+ LogInfo("EtcHostsDeleteOldEntries: Record %s not found in new, deleting", ARDisplayString(m, rr));
+ return mDNStrue;
+ }
+ // if primary is going away, make sure that the rest of the records
+ // point to the new primary
+ if (rr == ag->members)
+ {
+ AuthRecord *new_primary = rr->next;
+ AuthRecord *r = new_primary;
+ while (r)
+ {
+ if (r->RRSet == rr)
+ {
+ LogInfo("EtcHostsDeleteOldEntries: Updating Resource Record %s to primary", ARDisplayString(m, r));
+ r->RRSet = new_primary;
+ }
+ else LogMsg("EtcHostsDeleteOldEntries: ERROR!! Resource Record %s not pointing to primary %##s", ARDisplayString(m, r), r->resrec.name);
+ r = r->next;
+ }
+ }
+ LogInfo("EtcHostsDeleteOldEntries: Deleting %s", ARDisplayString(m, rr));
+ mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
+ }
+ }
+ return mDNSfalse;
+ }
+
+mDNSlocal void UpdateEtcHosts(mDNS *const m, void *context)
+ {
+ AuthHash *newhosts = (AuthHash *)context;
+
+ if (!m->mDNS_busy) LogMsg("UpdateEtcHosts: ERROR!! Lock not held");
+
+ //Delete old entries from the core if they are not present in the newhosts
+ EtcHostsDeleteOldEntries(m, newhosts, mDNSfalse);
+ // Add the new entries to the core if not already present in the core
+ EtcHostsAddNewEntries(m, newhosts, mDNSfalse);
+ }
+
+mDNSlocal void FreeNewHosts(AuthHash *newhosts)
+ {
+ mDNSu32 slot;
+ AuthGroup *ag, *agnext;
+ AuthRecord *rr, *rrnext;
+
+ for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+ for (ag = newhosts->rrauth_hash[slot]; ag; ag = agnext)
+ {
+ agnext = ag->next;
+ for (rr = ag->members; rr; rr = rrnext)
+ {
+ rrnext = rr->next;
+ freeL("etchosts", rr);
+ }
+ freeL("AuthGroups", ag);
+ }
+ }
+
+mDNSlocal void mDNSMacOSXUpdateEtcHosts(mDNS *const m)
+ {
+ AuthHash newhosts;
+
+ // As we will be modifying the core, we can only have one thread running at
+ // any point in time.
+ KQueueLock(m);
+
+ mDNSPlatformMemZero(&newhosts, sizeof(AuthHash));
+
+ // Get the file desecriptor (will trigger us to start watching for changes)
+ int fd = mDNSMacOSXGetEtcHostsFD(m);
+ if (fd != -1)
+ {
+ LogInfo("mDNSMacOSXUpdateEtcHosts: Parsing /etc/hosts fd %d", fd);
+ mDNSMacOSXParseEtcHosts(m, fd, &newhosts);
+ }
+ else LogInfo("mDNSMacOSXUpdateEtcHosts: /etc/hosts is not present");
+
+ // Optimization: Detect whether /etc/hosts changed or not.
+ //
+ // 1. Check to see if there are any new entries. We do this by seeing whether any entries in
+ // newhosts is already registered with core. If we find at least one entry that is not
+ // registered with core, then it means we have work to do.
+ //
+ // 2. Next, we check to see if any of the entries that are registered with core is not present
+ // in newhosts. If we find at least one entry that is not present, it means we have work to
+ // do.
+ //
+ // Note: We may not have to hold the lock right here as KQueueLock is held which prevents any
+ // other thread from running. But mDNS_Lock is needed here as we will be traversing the core
+ // data structure in EtcHostsDeleteOldEntries/NewEntries which might expect the lock to be held
+ // in the future and this code does not have to change.
+ mDNS_Lock(m);
+ // Add the new entries to the core if not already present in the core
+ if (!EtcHostsAddNewEntries(m, &newhosts, mDNStrue))
+ {
+ // No new entries to add, check to see if we need to delete any old entries from the
+ // core if they are not present in the newhosts
+ if (!EtcHostsDeleteOldEntries(m, &newhosts, mDNStrue))
+ {
+ LogInfo("mDNSMacOSXUpdateEtcHosts: No work");
+ mDNS_Unlock(m);
+ KQueueUnlock(m, "/etc/hosts changed");
+ FreeNewHosts(&newhosts);
+ return;
+ }
+ }
+
+ // This will flush the cache, stop and start the query so that the queries
+ // can look at the /etc/hosts again
+ //
+ // Notes:
+ //
+ // We can't delete and free the records here. We wait for the mDNSCoreRestartAddressQueries to
+ // deliver RMV events. It has to be done in a deferred way because we can't deliver RMV
+ // events for local records *before* the RMV events for cache records. mDNSCoreRestartAddressQueries
+ // delivers these events in the right order and then calls us back to delete them.
+ //
+ // Similarly, we do a deferred Registration of the record because mDNSCoreRestartAddressQueries
+ // is a common function that looks at all local auth records and delivers a RMV including
+ // the records that we might add here. If we deliver a ADD here, it will get a RMV and then when
+ // the query is restarted, it will get another ADD. To avoid this (ADD-RMV-ADD), we defer registering
+ // the record until the RMVs are delivered in mDNSCoreRestartAddressQueries after which UpdateEtcHosts
+ // is called back where we do the Registration of the record. This results in RMV followed by ADD which
+ // looks normal.
+ mDNSCoreRestartAddressQueries(m, mDNSfalse, FlushAllCacheRecords, UpdateEtcHosts, &newhosts);
+ mDNS_Unlock(m);
+
+ KQueueUnlock(m, "/etc/hosts changed");
+ FreeNewHosts(&newhosts);
+ }
+
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark -
#pragma mark - Initialization & Teardown
// Major version 8 is 10.4.x (Tiger)
// Major version 9 is 10.5.x (Leopard)
// Major version 10 is 10.6.x (SnowLeopard)
-mDNSexport int mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring)
+mDNSexport void mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring)
{
int major = 0, minor = 0;
char letter = 0, prodname[256]="<Unknown>", prodvers[256]="<Unknown>", buildver[256]="<Unknown>";
}
if (!major) { major=8; LogMsg("Note: No Major Build Version number found; assuming 8"); }
if (HINFO_SWstring) mDNS_snprintf(HINFO_SWstring, 256, "%s %s (%s), %s", prodname, prodvers, buildver, STRINGIFY(mDNSResponderVersion));
- return(major);
+ //LogMsg("%s %s (%s), %d %c %d", prodname, prodvers, buildver, major, letter, minor);
+
+ // If product name is "Mac OS X" (or similar) we set OSXVers, else we set iOSVers;
+ if ((prodname[0] & 0xDF) == 'M') OSXVers = major; else iOSVers = major;
}
// Test to see if we're the first client running on UDP port 5353, by trying to bind to 5353 without using SO_REUSEPORT.
m->p->CFRunLoop = CFRunLoopGetCurrent();
char HINFO_SWstring[256] = "";
- OSXVers = mDNSMacOSXSystemBuildNumber(HINFO_SWstring);
+ mDNSMacOSXSystemBuildNumber(HINFO_SWstring);
// In 10.4, mDNSResponder is launched very early in the boot process, while other subsystems are still in the process of starting up.
// If we can't read the user's preferences, then we sleep a bit and try again, for up to five seconds before we give up.
// if (sysctl(get_model, 2, HINFO_HWstring_buffer, &len_model, NULL, 0) == 0 && strchr(HINFO_HWstring_buffer, ','))
if (sysctl(get_model, 2, HINFO_HWstring_buffer, &len_model, NULL, 0) == 0)
HINFO_HWstring = HINFO_HWstring_buffer;
- HINFO_HWstring_prefixlen = strcspn(HINFO_HWstring, "0123456789");
- if (OSXVers < OSXVers_10_3_Panther ) m->KnownBugs |= mDNS_KnownBug_PhantomInterfaces;
- if (OSXVers <= OSXVers_10_6_SnowLeopard ) m->KnownBugs |= mDNS_KnownBug_LimitedIPv6;
- if (OSXVers >= OSXVers_10_6_SnowLeopard ) m->KnownBugs |= mDNS_KnownBug_LossySyslog;
+ // For names of the form "iPhone2,1" we use "iPhone" as the prefix for automatic name generation.
+ // For names of the form "N88AP" containg no comma, we use the entire string.
+ HINFO_HWstring_prefixlen = strchr(HINFO_HWstring_buffer, ',') ? strcspn(HINFO_HWstring, "0123456789") : strlen(HINFO_HWstring);
+
+ if (OSXVers && OSXVers <= OSXVers_10_6_SnowLeopard) m->KnownBugs |= mDNS_KnownBug_LimitedIPv6;
+ if (OSXVers && OSXVers >= OSXVers_10_6_SnowLeopard) m->KnownBugs |= mDNS_KnownBug_LossySyslog;
if (mDNSPlatformInit_CanReceiveUnicast()) m->CanReceiveUnicastOn5353 = mDNStrue;
mDNSu32 hlen = mDNSPlatformStrLen(HINFO_HWstring);
m->p->prevoldhostlabel.c[0] = 0;
m->p->prevnewhostlabel.c[0] = 0;
m->p->NotifyUser = 0;
- m->p->KeyChainBugTimer = 0;
+ m->p->KeyChainTimer = 0;
m->p->WakeAtUTC = 0;
m->p->RequestReSleep = 0;
if (!m->p->PowerConnection) { LogMsg("mDNSPlatformInit_setup: IORegisterForSystemPower failed"); return(-1); }
else
{
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
IONotificationPortSetDispatchQueue(m->p->PowerPortRef, dispatch_get_main_queue());
#else
CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(m->p->PowerPortRef), kCFRunLoopDefaultMode);
-#endif /* __LIB_DISPATCH__ */
+#endif /* MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM */
}
}
else if (!strncasecmp(HINFO_HWstring, "TimeCapsule", 11)) { SPMetricPortability = 34 /* 4kg */; SPMetricMarginalPower = 10 /* ~0W */; SPMetricTotalPower = 70 /* 13W */; }
else if (!strncasecmp(HINFO_HWstring, "AirPort", 7)) { SPMetricPortability = 35 /* 3kg */; SPMetricMarginalPower = 10 /* ~0W */; SPMetricTotalPower = 70 /* 12W */; }
else if (!strncasecmp(HINFO_HWstring, "AppleTV", 7)) { SPMetricPortability = 35 /* 3kg */; SPMetricMarginalPower = 10 /* ~0W */; SPMetricTotalPower = 73 /* 20W */; }
+ else if (!strncasecmp(HINFO_HWstring, "K66AP", 5)) { SPMetricPortability = 35 /* 3kg */; SPMetricMarginalPower = 60 /* 1W */; SPMetricTotalPower = 63 /* 2W */; }
else if (!strncasecmp(HINFO_HWstring, "MacBook", 7)) { SPMetricPortability = 37 /* 2kg */; SPMetricMarginalPower = 71 /* 13W */; SPMetricTotalPower = 72 /* 15W */; }
else if (!strncasecmp(HINFO_HWstring, "PowerBook", 9)) { SPMetricPortability = 37 /* 2kg */; SPMetricMarginalPower = 71 /* 13W */; SPMetricTotalPower = 72 /* 15W */; }
LogSPS("HW_MODEL: %.*s (%s) Portability %d Marginal Power %d Total Power %d",
m->p->ConndBTMMDict = mDNSNULL;
#endif // APPLE_OSX_mDNSResponder
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
// Currently this is not defined. SSL code will eventually fix this. If it becomes
// critical, we will define this to workaround the bug in SSL.
#ifdef __SSL_NEEDS_SERIALIZATION__
#endif
if (SSLqueue == mDNSNULL) LogMsg("dispatch_queue_create: SSL queue NULL");
#endif
+ mDNSMacOSXUpdateEtcHosts(m);
return(mStatus_NoError);
}
{
if (m->p->PowerConnection)
{
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
IONotificationPortSetDispatchQueue(m->p->PowerPortRef, NULL);
#else
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(m->p->PowerPortRef), kCFRunLoopDefaultMode);
if (m->p->Store)
{
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
if (!SCDynamicStoreSetDispatchQueue(m->p->Store, NULL))
LogMsg("mDNSPlatformClose: SCDynamicStoreSetDispatchQueue failed");
#else
{
AnonymousRacoonConfig = mDNSNULL;
LogInfo("mDNSPlatformClose: Deconfiguring autotunnel");
- (void)mDNSConfigureServer(kmDNSDown, mDNSNULL);
+ (void)mDNSConfigureServer(kmDNSDown, mDNSNULL, mDNSNULL);
}
if (m->AutoTunnelHostAddrActive && m->AutoTunnelHostAddr.b[0])
LogMsg("mDNSPlatformRawTime: this_mach_absolute_time %08X%08X", this_mach_absolute_time);
// Update last_mach_absolute_time *before* calling NotifyOfElusiveBug()
last_mach_absolute_time = this_mach_absolute_time;
- // Only show "mach_absolute_time went backwards" notice on 10.4 (build 8xyyy) or later.
- // (This bug happens all the time on 10.3, and we know that's not going to be fixed.)
- if (OSXVers >= OSXVers_10_4_Tiger)
- NotifyOfElusiveBug("mach_absolute_time went backwards!",
- "This error occurs from time to time, often on newly released hardware, "
- "and usually the exact cause is different in each instance.\r\r"
- "Please file a new Radar bug report with the title “mach_absolute_time went backwards” "
- "and assign it to Radar Component “Kernel” Version “X”.");
+ // Note: This bug happens all the time on 10.3
+ NotifyOfElusiveBug("mach_absolute_time went backwards!",
+ "This error occurs from time to time, often on newly released hardware, "
+ "and usually the exact cause is different in each instance.\r\r"
+ "Please file a new Radar bug report with the title “mach_absolute_time went backwards” "
+ "and assign it to Radar Component “Kernel” Version “X”.");
}
last_mach_absolute_time = this_mach_absolute_time;
#endif
mDNSexport void mDNSPlatformMemFree (void *mem) { freeL("mDNSPlatformMemFree", mem); }
-mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep)
+mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason)
{
if (allowSleep && m->p->IOPMAssertion)
{
}
else if (!allowSleep && m->p->IOPMAssertion == 0)
{
- IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, CFSTR("mDNSResponder"), &m->p->IOPMAssertion);
+#ifdef kIOPMAssertionTypeNoIdleSleep
+ CFStringRef assertionName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s.%d %s"), getprogname(), getpid(), reason ? reason : "");
+ IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, assertionName ? assertionName : CFSTR("mDNSResponder"), &m->p->IOPMAssertion);
+ if (assertionName) CFRelease(assertionName);
LogInfo("%s Creating NoIdleSleep power assertion", __FUNCTION__);
+#endif
}
}
mDNSu32 ifindex;
// Sanity check
- ifindex = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID);
+ ifindex = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNStrue);
if (ifindex <= 0)
{
LogMsg("mDNSPlatformSendWakeupPacket: ERROR!! Invalid InterfaceID %u", ifindex);
}
mDNSSendWakeupPacket(ifindex, EthAddr, IPAddr, iteration);
}
+
+// Called for rr->InterfaceID == mDNSInterface_Any.
+// If current interface is P2P, verify that record is marked to IncludeP2P.
+mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf)
+ {
+ mDNSBool p2pInterface = (strncmp(intf->ifname, "p2p", 3) == 0);
+
+ if (!p2pInterface || (rr->ARType == AuthRecordAnyIncludeP2P))
+ return mDNStrue;
+ else
+ return mDNSfalse;
+ }
#include <netinet/in.h>
#include "mDNSEmbeddedAPI.h" // for domain name structure
-//#define __LIB_DISPATCH__
-#ifdef __LIB_DISPATCH__
+//#define MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
#include <dispatch/dispatch.h>
#endif
KQueueEventCallback KQcallback;
void *KQcontext;
const char const *KQtask; // For debugging messages
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
dispatch_source_t readSource;
dispatch_source_t writeSource;
mDNSBool fdClosed;
int BPF_fd; // -1 uninitialized; -2 requested BPF; -3 failed
int BPF_mcfd; // Socket for our IPv6 ND group membership
u_int BPF_len;
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
dispatch_source_t BPF_source;
#else
CFSocketRef BPF_cfs;
mDNSs32 NotifyUser;
mDNSs32 HostNameConflict; // Time we experienced conflict on our link-local host name
mDNSs32 NetworkChanged;
-
- // KeyChain frequently fails to notify clients of change events. To work around this
- // we set a timer and periodically poll to detect if any changes have occurred.
- // Without this Back To My Mac just does't work for a large number of users.
- // See <rdar://problem/5124399> Not getting Keychain Changed events when enabling BTMM
- mDNSs32 KeyChainBugTimer;
- mDNSs32 KeyChainBugInterval;
+ mDNSs32 KeyChainTimer;
CFRunLoopRef CFRunLoop;
SCDynamicStoreRef Store;
long SleepCookie; // Cookie we need to pass to IOAllowPowerChange()
long WakeAtUTC;
mDNSs32 RequestReSleep;
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
dispatch_source_t timer;
dispatch_source_t custom;
#else
extern int OfferSleepProxyService;
extern int DisableSleepProxyClient;
+extern int UseInternalSleepProxy;
+extern int OSXVers, iOSVers;
extern mDNSBool DisableInboundRelayConnection;
-extern int OSXVers;
#define OSXVers_Base 4
#define OSXVers_10_0_Cheetah 4
#define OSXVers_10_1_Puma 5
extern void NotifyOfElusiveBug(const char *title, const char *msg); // Both strings are UTF-8 text
extern void SetDomainSecrets(mDNS *m);
extern void mDNSMacOSXNetworkChanged(mDNS *const m);
-extern int mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring);
+extern void mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring);
extern NetworkInterfaceInfoOSX *IfindexToInterfaceInfoOSX(const mDNS *const m, mDNSInterfaceID ifindex);
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
extern int KQueueSet(int fd, u_short flags, short filter, KQueueEntry *const entryRef);
mDNSexport void TriggerEventCompletion(void);
#else
(allow file-read-data (regex #"^/dev/random$"))
(allow file-read-data file-write-data (regex #"^/dev/console$")) ; Needed for syslog early in the boot process
(allow file-read-data (regex #"^/dev/autofs_nowait$")) ; Used by CF to circumvent automount triggers
+(allow file-read-data (regex #"^/private/etc/hosts$")) ; /etc/hosts support
+(allow file-read-data (regex #"^/private/etc$")) ; /etc/hosts support
; Allow us to read and write our socket
(allow file-read* file-write* (regex #"^/private/var/run/mDNSResponder$"))
; CRL Cache for SSL/TLS connections
(allow file-read-data (regex #"^/private/var/db/crls/crlcache\.db$"))
-; For mDNS sleep proxy offload
-(allow iokit-open (iokit-user-client-class "NVEthernetUserClientMDNS"))
-(allow iokit-open (iokit-user-client-class "mDNSOffloadUserClient"))
-
-; For IOPMConnectionCreate
-(allow iokit-open (iokit-user-client-class "RootDomainUserClient"))
+; For mDNS sleep proxy offload and IOPMConnectionCreate
+(if (defined? 'iokit-open)
+ (begin
+ (allow iokit-open (iokit-user-client-class "NVEthernetUserClientMDNS"))
+ (allow iokit-open (iokit-user-client-class "mDNSOffloadUserClient"))
+ (allow iokit-open (iokit-user-client-class "RootDomainUserClient"))))
; For D2D
(allow file-read-data (regex #"^/System/Library/PrivateFrameworks/DeviceToDeviceManager.framework(/|$)"))
D284BF300ADD81630027CCDF /* PBXTargetDependency */,
D284BF260ADD814F0027CCDF /* PBXTargetDependency */,
D284BF2A0ADD81530027CCDF /* PBXTargetDependency */,
+ 216EF8EC120295AA004917D2 /* PBXTargetDependency */,
);
name = "Build More";
productName = "Build All";
name = "Build Some";
productName = "Build Some";
};
- FFA572650AF190F10055A0F1 /* SystemLibraries */ = {
+ 2141DCF8123FFB5D0086D23E /* SystemLibraries */ = {
isa = PBXAggregateTarget;
- buildConfigurationList = FFA5726E0AF191200055A0F1 /* Build configuration list for PBXAggregateTarget "SystemLibraries" */;
+ buildConfigurationList = 2141DD08123FFB830086D23E /* Build configuration list for PBXAggregateTarget "SystemLibraries" */;
buildPhases = (
);
+ dependencies = (
+ 2141DD0E123FFC960086D23E /* PBXTargetDependency */,
+ 2130257112400E9300AC839F /* PBXTargetDependency */,
+ );
+ name = SystemLibraries;
+ productName = SystemLibraries;
+ };
+ 2141DD0B123FFC7F0086D23E /* SystemLibrariesStatic */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = 2141DD18123FFC990086D23E /* Build configuration list for PBXAggregateTarget "SystemLibrariesStatic" */;
+ buildPhases = (
+ );
+ dependencies = (
+ 215FFB1D124002CC00470DE1 /* PBXTargetDependency */,
+ 215FFB1B124002C700470DE1 /* PBXTargetDependency */,
+ 215FFB19124002C100470DE1 /* PBXTargetDependency */,
+ );
+ name = SystemLibrariesStatic;
+ productName = SystemLibrariesStatic;
+ };
+ FFA572650AF190F10055A0F1 /* SystemLibrariesDynamic */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = FFA5726E0AF191200055A0F1 /* Build configuration list for PBXAggregateTarget "SystemLibrariesDynamic" */;
+ buildPhases = (
+ 1F7B473C12B82BFD00868AEF /* ShellScript */,
+ );
dependencies = (
FFA572690AF190FF0055A0F1 /* PBXTargetDependency */,
FFA5726B0AF191010055A0F1 /* PBXTargetDependency */,
FFA5726D0AF191020055A0F1 /* PBXTargetDependency */,
);
- name = SystemLibraries;
+ name = SystemLibrariesDynamic;
productName = SystemLibraries;
};
FFB7657B0AEED96B00583A2C /* Build All */ = {
);
dependencies = (
FFB7657D0AEED97F00583A2C /* PBXTargetDependency */,
- FFA572710AF191230055A0F1 /* PBXTargetDependency */,
+ 2141DCFD123FFB7D0086D23E /* PBXTargetDependency */,
);
name = "Build All";
productName = "Build All";
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
+ 213FB23C12028C4A002B3A08 /* BonjourEvents.c in Sources */ = {isa = PBXBuildFile; fileRef = 213FB22C12028B53002B3A08 /* BonjourEvents.c */; };
+ 213FB23D12028C5A002B3A08 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09AB6884FE841BABC02AAC07 /* CoreFoundation.framework */; };
+ 215FFAEE124000F900470DE1 /* dnssd_ipc.c in Sources */ = {isa = PBXBuildFile; fileRef = F5E11B5A04A28126019798ED /* dnssd_ipc.c */; };
+ 215FFAEF124000F900470DE1 /* dnssd_clientlib.c in Sources */ = {isa = PBXBuildFile; fileRef = FFFA38620AEEDB090065B80A /* dnssd_clientlib.c */; };
+ 215FFAF0124000F900470DE1 /* dnssd_clientstub.c in Sources */ = {isa = PBXBuildFile; fileRef = FFFA38640AEEDB130065B80A /* dnssd_clientstub.c */; };
+ 215FFAF1124000F900470DE1 /* DNSServiceDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */; };
+ 215FFAF2124000F900470DE1 /* DNSServiceDiscoveryRequest.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC01022EAFBA00000109 /* DNSServiceDiscoveryRequest.defs */; };
+ 215FFAF3124000F900470DE1 /* DNSServiceDiscoveryReply.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC00022EAFBA00000109 /* DNSServiceDiscoveryReply.defs */; settings = {ATTRIBUTES = (Server, ); }; };
+ 215FFAF41240011800470DE1 /* dnssd_ipc.c in Sources */ = {isa = PBXBuildFile; fileRef = F5E11B5A04A28126019798ED /* dnssd_ipc.c */; };
+ 215FFAF51240011800470DE1 /* dnssd_clientlib.c in Sources */ = {isa = PBXBuildFile; fileRef = FFFA38620AEEDB090065B80A /* dnssd_clientlib.c */; };
+ 215FFAF61240011800470DE1 /* dnssd_clientstub.c in Sources */ = {isa = PBXBuildFile; fileRef = FFFA38640AEEDB130065B80A /* dnssd_clientstub.c */; };
+ 215FFAF71240011800470DE1 /* DNSServiceDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */; };
+ 215FFAF81240011800470DE1 /* DNSServiceDiscoveryRequest.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC01022EAFBA00000109 /* DNSServiceDiscoveryRequest.defs */; };
+ 215FFAF91240011800470DE1 /* DNSServiceDiscoveryReply.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC00022EAFBA00000109 /* DNSServiceDiscoveryReply.defs */; settings = {ATTRIBUTES = (Server, ); }; };
+ 215FFAFA1240013400470DE1 /* dnssd_ipc.c in Sources */ = {isa = PBXBuildFile; fileRef = F5E11B5A04A28126019798ED /* dnssd_ipc.c */; };
+ 215FFAFB1240013400470DE1 /* dnssd_clientlib.c in Sources */ = {isa = PBXBuildFile; fileRef = FFFA38620AEEDB090065B80A /* dnssd_clientlib.c */; };
+ 215FFAFC1240013400470DE1 /* dnssd_clientstub.c in Sources */ = {isa = PBXBuildFile; fileRef = FFFA38640AEEDB130065B80A /* dnssd_clientstub.c */; };
+ 215FFAFD1240013400470DE1 /* DNSServiceDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */; };
+ 215FFAFE1240013400470DE1 /* DNSServiceDiscoveryRequest.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC01022EAFBA00000109 /* DNSServiceDiscoveryRequest.defs */; };
+ 215FFAFF1240013400470DE1 /* DNSServiceDiscoveryReply.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC00022EAFBA00000109 /* DNSServiceDiscoveryReply.defs */; settings = {ATTRIBUTES = (Server, ); }; };
2E0405F50C3195F700F13B59 /* helper.c in Sources */ = {isa = PBXBuildFile; fileRef = 2E0405F40C3195F700F13B59 /* helper.c */; };
2E0405F60C31961100F13B59 /* helpermsg.defs in Sources */ = {isa = PBXBuildFile; fileRef = 2E0405EB0C3190DC00F13B59 /* helpermsg.defs */; settings = {ATTRIBUTES = (Server, Client, ); COMPILER_FLAGS = "-Wno-error"; }; };
2E0406150C3197CB00F13B59 /* libbsm.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 2E0406140C3197CB00F13B59 /* libbsm.dylib */; };
4AE9B0930F3A52A20080B59D /* safe_vproc.c in Sources */ = {isa = PBXBuildFile; fileRef = 4AE9B0480F39448B0080B59D /* safe_vproc.c */; };
4AE9B0940F3A52AE0080B59D /* safe_vproc.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AE9B0490F39448B0080B59D /* safe_vproc.h */; };
4AE9B0950F3A52AE0080B59D /* safe_vproc.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AE9B0490F39448B0080B59D /* safe_vproc.h */; };
+ 4BD2B63A134FE09F002B96D5 /* P2PPacketFilter.c in Sources */ = {isa = PBXBuildFile; fileRef = 4BD2B638134FE09F002B96D5 /* P2PPacketFilter.c */; };
+ 4BD2B63B134FE09F002B96D5 /* P2PPacketFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BD2B639134FE09F002B96D5 /* P2PPacketFilter.h */; };
D284BE530ADD80740027CCDF /* DNSServiceDiscoveryDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 6575FBFF022EAFBA00000109 /* DNSServiceDiscoveryDefines.h */; };
D284BE540ADD80740027CCDF /* dnssd_ipc.h in Headers */ = {isa = PBXBuildFile; fileRef = F5E11B5B04A28126019798ED /* dnssd_ipc.h */; };
D284BE560ADD80740027CCDF /* DNSServiceDiscoveryReply.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC00022EAFBA00000109 /* DNSServiceDiscoveryReply.defs */; settings = {ATTRIBUTES = (Client, ); }; };
FFA572490AF18FCC0055A0F1 /* DNSServiceDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */; };
FFA5724A0AF18FCC0055A0F1 /* DNSServiceDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */; };
FFA5724B0AF18FCC0055A0F1 /* DNSServiceDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */; };
- FFA572610AF190940055A0F1 /* DNSServiceDiscovery.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = FFA572600AF1908D0055A0F1 /* DNSServiceDiscovery.h */; };
FFAE66F0105F0CD900162116 /* ddnswriteconfig in Resources */ = {isa = PBXBuildFile; fileRef = D284BEE80ADD80A70027CCDF /* ddnswriteconfig */; };
FFB437150EB165BD00E17C68 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 00CA213D02786FC30CCA2C71 /* IOKit.framework */; };
FFC22AA20B00F42A00BAB070 /* DNSServiceDiscoveryRequest.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC01022EAFBA00000109 /* DNSServiceDiscoveryRequest.defs */; };
remoteGlobalIDString = 03067D640C83A3700022BE1F;
remoteInfo = "Build Some";
};
+ 2130257012400E9300AC839F /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = FFA572650AF190F10055A0F1;
+ remoteInfo = SystemLibrariesDynamic;
+ };
+ 2141DCFC123FFB7D0086D23E /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 2141DCF8123FFB5D0086D23E;
+ remoteInfo = SystemLibraries;
+ };
+ 2141DD0D123FFC960086D23E /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 2141DD0B123FFC7F0086D23E;
+ remoteInfo = SystemLibrariesStatic;
+ };
+ 215FFB18124002C100470DE1 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 2141DD1C123FFCDB0086D23E;
+ remoteInfo = libdns_sd_static;
+ };
+ 215FFB1A124002C700470DE1 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 2141DD23123FFD0F0086D23E;
+ remoteInfo = libdns_sd_debug_static;
+ };
+ 215FFB1C124002CC00470DE1 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 2141DD29123FFD2C0086D23E;
+ remoteInfo = libdns_sd_profile_static;
+ };
+ 216EF8EB120295AA004917D2 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 213FB21712028A7A002B3A08;
+ remoteInfo = BonjourEvents;
+ };
4AE471690EAFF83800A6C5AD /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
remoteGlobalIDString = FFA5723C0AF18F450055A0F1;
remoteInfo = "libdns_sd profile";
};
- FFA572700AF191230055A0F1 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = FFA572650AF190F10055A0F1;
- remoteInfo = SystemLibraries;
- };
FFAE66F8105F0CF100162116 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
);
runOnlyForDeploymentPostprocessing = 1;
};
- FFA572500AF190070055A0F1 /* CopyFiles */ = {
- isa = PBXCopyFilesBuildPhase;
- buildActionMask = 8;
- dstPath = /usr/include/DNSServiceDiscovery;
- dstSubfolderSpec = 0;
- files = (
- FFA572610AF190940055A0F1 /* DNSServiceDiscovery.h in CopyFiles */,
- );
- runOnlyForDeploymentPostprocessing = 1;
- };
FFFF8F770C32F0FD00722979 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 8;
000753D303367C1C0CCA2C71 /* mDNSMacOSX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mDNSMacOSX.h; sourceTree = "<group>"; };
00CA213D02786FC30CCA2C71 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = "<absolute>"; };
09AB6884FE841BABC02AAC07 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = "<absolute>"; };
+ 213FB21812028A7A002B3A08 /* BonjourEvents.plugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BonjourEvents.plugin; sourceTree = BUILT_PRODUCTS_DIR; };
+ 213FB22C12028B53002B3A08 /* BonjourEvents.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = BonjourEvents.c; sourceTree = "<group>"; };
+ 213FB22D12028B53002B3A08 /* BonjourEvents-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "BonjourEvents-Info.plist"; sourceTree = "<group>"; };
+ 2141DD1D123FFCDB0086D23E /* libdns_sd.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdns_sd.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 2141DD24123FFD0F0086D23E /* libdns_sd_debug.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdns_sd_debug.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 2141DD2A123FFD2C0086D23E /* libdns_sd_profile.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdns_sd_profile.a; sourceTree = BUILT_PRODUCTS_DIR; };
21F432971134AA6800581B69 /* WebFilterDNS.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebFilterDNS.framework; path = /System/Library/PrivateFrameworks/WebFilterDNS.framework; sourceTree = "<absolute>"; };
2E0405EB0C3190DC00F13B59 /* helpermsg.defs */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.mig; path = helpermsg.defs; sourceTree = "<group>"; };
2E0405F00C31955500F13B59 /* mDNSResponderHelper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mDNSResponderHelper; sourceTree = BUILT_PRODUCTS_DIR; };
4ADB5F230F6AB9F400B95BF3 /* helper-entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "helper-entitlements.plist"; sourceTree = "<group>"; };
4AE9B0480F39448B0080B59D /* safe_vproc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = safe_vproc.c; sourceTree = "<group>"; };
4AE9B0490F39448B0080B59D /* safe_vproc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = safe_vproc.h; sourceTree = "<group>"; };
+ 4BD2B638134FE09F002B96D5 /* P2PPacketFilter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = P2PPacketFilter.c; sourceTree = "<group>"; };
+ 4BD2B639134FE09F002B96D5 /* P2PPacketFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = P2PPacketFilter.h; sourceTree = "<group>"; };
654BE64F02B63B93000001D1 /* mDNSEmbeddedAPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mDNSEmbeddedAPI.h; path = ../mDNSCore/mDNSEmbeddedAPI.h; sourceTree = "<group>"; };
654BE65002B63B93000001D1 /* mDNSDebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mDNSDebug.h; path = ../mDNSCore/mDNSDebug.h; sourceTree = "<group>"; };
65713D46025A293200000109 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = /System/Library/Frameworks/SystemConfiguration.framework; sourceTree = "<absolute>"; };
FF485D5105632E0000130380 /* mDNSResponder.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = mDNSResponder.8; path = ../mDNSShared/mDNSResponder.8; sourceTree = SOURCE_ROOT; };
FF5852100DD27BD300862BDF /* ClientCommon.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = ClientCommon.c; path = ../Clients/ClientCommon.c; sourceTree = SOURCE_ROOT; };
FF85880B0BD599F40080D89F /* mDNSResponder.sb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = mDNSResponder.sb; sourceTree = SOURCE_ROOT; };
- FFA572390AF18F1C0055A0F1 /* libdns_sd_debug.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdns_sd_debug.a; sourceTree = BUILT_PRODUCTS_DIR; };
- FFA572450AF18F450055A0F1 /* libdns_sd_profile.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdns_sd_profile.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ FFA572390AF18F1C0055A0F1 /* libsystem_dnssd_debug.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libsystem_dnssd_debug.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
+ FFA572450AF18F450055A0F1 /* libsystem_dnssd_profile.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libsystem_dnssd_profile.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = DNSServiceDiscovery.c; sourceTree = "<group>"; };
FFA572600AF1908D0055A0F1 /* DNSServiceDiscovery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNSServiceDiscovery.h; sourceTree = "<group>"; };
FFA572630AF190C20055A0F1 /* dns_sd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dns_sd.h; path = ../mDNSShared/dns_sd.h; sourceTree = SOURCE_ROOT; };
- FFB765840AEED9C700583A2C /* libdns_sd.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdns_sd.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ FFB765840AEED9C700583A2C /* libsystem_dnssd.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libsystem_dnssd.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
FFCB6D73075D539900B8AF62 /* PlatformCommon.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = PlatformCommon.c; path = ../mDNSShared/PlatformCommon.c; sourceTree = SOURCE_ROOT; };
FFE6935007C2CA7F00283007 /* ConfigurationAuthority.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ConfigurationAuthority.h; path = PreferencePane/ConfigurationAuthority.h; sourceTree = SOURCE_ROOT; };
FFE6935207C2CAA400283007 /* DNSServiceDiscoveryPref.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DNSServiceDiscoveryPref.h; path = PreferencePane/DNSServiceDiscoveryPref.h; sourceTree = SOURCE_ROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
+ 213FB21612028A7A002B3A08 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 213FB23D12028C5A002B3A08 /* CoreFoundation.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 2141DD1B123FFCDB0086D23E /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 2141DD22123FFD0F0086D23E /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 2141DD28123FFD2C0086D23E /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
2E0405EE0C31955500F13B59 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
children = (
08FB7795FE84155DC02AAC07 /* mDNS Server Sources */,
6575FC1F022EB78C00000109 /* Command-Line Clients */,
+ 213FB20912028902002B3A08 /* Bonjour Events Plugin */,
6575FBFE022EAFA800000109 /* MIG files */,
DB2CC4420662DCE500335AB3 /* Java Support */,
FFFB0DA407B43BED00B88D48 /* PreferencePane */,
FFA572600AF1908D0055A0F1 /* DNSServiceDiscovery.h */,
FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */,
FFA572630AF190C20055A0F1 /* dns_sd.h */,
+ 4BD2B638134FE09F002B96D5 /* P2PPacketFilter.c */,
+ 4BD2B639134FE09F002B96D5 /* P2PPacketFilter.h */,
);
name = "mDNS Server Sources";
sourceTree = "<group>";
D284BED90ADD80A20027CCDF /* dnsextd */,
D284BEE80ADD80A70027CCDF /* ddnswriteconfig */,
D284BF0C0ADD80B00027CCDF /* Bonjour.prefPane */,
- FFB765840AEED9C700583A2C /* libdns_sd.a */,
- FFA572390AF18F1C0055A0F1 /* libdns_sd_debug.a */,
- FFA572450AF18F450055A0F1 /* libdns_sd_profile.a */,
+ FFB765840AEED9C700583A2C /* libsystem_dnssd.dylib */,
+ FFA572390AF18F1C0055A0F1 /* libsystem_dnssd_debug.dylib */,
+ FFA572450AF18F450055A0F1 /* libsystem_dnssd_profile.dylib */,
2E0405F00C31955500F13B59 /* mDNSResponderHelper */,
+ 213FB21812028A7A002B3A08 /* BonjourEvents.plugin */,
+ 2141DD1D123FFCDB0086D23E /* libdns_sd.a */,
+ 2141DD24123FFD0F0086D23E /* libdns_sd_debug.a */,
+ 2141DD2A123FFD2C0086D23E /* libdns_sd_profile.a */,
);
name = Products;
sourceTree = "<group>";
};
+ 213FB20912028902002B3A08 /* Bonjour Events Plugin */ = {
+ isa = PBXGroup;
+ children = (
+ 213FB22C12028B53002B3A08 /* BonjourEvents.c */,
+ 213FB22D12028B53002B3A08 /* BonjourEvents-Info.plist */,
+ );
+ name = "Bonjour Events Plugin";
+ sourceTree = "<group>";
+ };
6575FBFE022EAFA800000109 /* MIG files */ = {
isa = PBXGroup;
children = (
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
+ 2141DD19123FFCDB0086D23E /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 2141DD20123FFD0F0086D23E /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 2141DD26123FFD2C0086D23E /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
2EC8F8ED0C39CCCA003C9C48 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
2E8165E80C5980E300485EB2 /* libpfkey.h in Headers */,
2E8165EA0C5980F700485EB2 /* ipsec_strerror.h in Headers */,
4AE9B04C0F39448B0080B59D /* safe_vproc.h in Headers */,
+ 4BD2B63B134FE09F002B96D5 /* P2PPacketFilter.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXLegacyTarget section */
/* Begin PBXNativeTarget section */
+ 213FB21712028A7A002B3A08 /* BonjourEvents */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 213FB21B12028A7C002B3A08 /* Build configuration list for PBXNativeTarget "BonjourEvents" */;
+ buildPhases = (
+ 213FB21412028A7A002B3A08 /* Resources */,
+ 213FB21512028A7A002B3A08 /* Sources */,
+ 213FB21612028A7A002B3A08 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = BonjourEvents;
+ productName = BonjourEvents;
+ productReference = 213FB21812028A7A002B3A08 /* BonjourEvents.plugin */;
+ productType = "com.apple.product-type.bundle";
+ };
+ 2141DD1C123FFCDB0086D23E /* libdns_sd_static */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 2141DD1F123FFCF90086D23E /* Build configuration list for PBXNativeTarget "libdns_sd_static" */;
+ buildPhases = (
+ 2141DD19123FFCDB0086D23E /* Headers */,
+ 2141DD1A123FFCDB0086D23E /* Sources */,
+ 2141DD1B123FFCDB0086D23E /* Frameworks */,
+ 2130256B12400DE600AC839F /* ShellScript */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = libdns_sd_static;
+ productName = libdns_sd_static;
+ productReference = 2141DD1D123FFCDB0086D23E /* libdns_sd.a */;
+ productType = "com.apple.product-type.library.static";
+ };
+ 2141DD23123FFD0F0086D23E /* libdns_sd_debug_static */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 2141DD35123FFD3B0086D23E /* Build configuration list for PBXNativeTarget "libdns_sd_debug_static" */;
+ buildPhases = (
+ 2141DD20123FFD0F0086D23E /* Headers */,
+ 2141DD21123FFD0F0086D23E /* Sources */,
+ 2141DD22123FFD0F0086D23E /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = libdns_sd_debug_static;
+ productName = libdns_sd_debug_static;
+ productReference = 2141DD24123FFD0F0086D23E /* libdns_sd_debug.a */;
+ productType = "com.apple.product-type.library.static";
+ };
+ 2141DD29123FFD2C0086D23E /* libdns_sd_profile_static */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 2141DD36123FFD3B0086D23E /* Build configuration list for PBXNativeTarget "libdns_sd_profile_static" */;
+ buildPhases = (
+ 2141DD26123FFD2C0086D23E /* Headers */,
+ 2141DD27123FFD2C0086D23E /* Sources */,
+ 2141DD28123FFD2C0086D23E /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = libdns_sd_profile_static;
+ productName = libdns_sd_profile_static;
+ productReference = 2141DD2A123FFD2C0086D23E /* libdns_sd_profile.a */;
+ productType = "com.apple.product-type.library.static";
+ };
2E0405EF0C31955500F13B59 /* mDNSResponderHelper */ = {
isa = PBXNativeTarget;
buildConfigurationList = 2E0405F30C31956600F13B59 /* Build configuration list for PBXNativeTarget "mDNSResponderHelper" */;
productReference = D284BF0C0ADD80B00027CCDF /* Bonjour.prefPane */;
productType = "com.apple.product-type.bundle";
};
- FFA572300AF18F1C0055A0F1 /* libdns_sd debug */ = {
+ FFA572300AF18F1C0055A0F1 /* libdns_sd_debug_dynamic */ = {
isa = PBXNativeTarget;
- buildConfigurationList = FFA572370AF18F1C0055A0F1 /* Build configuration list for PBXNativeTarget "libdns_sd debug" */;
+ buildConfigurationList = FFA572370AF18F1C0055A0F1 /* Build configuration list for PBXNativeTarget "libdns_sd_debug_dynamic" */;
buildPhases = (
FFA572310AF18F1C0055A0F1 /* Headers */,
FFA572320AF18F1C0055A0F1 /* Sources */,
);
dependencies = (
);
- name = "libdns_sd debug";
- productName = libdns_sd.a;
- productReference = FFA572390AF18F1C0055A0F1 /* libdns_sd_debug.a */;
- productType = "com.apple.product-type.library.static";
+ name = libdns_sd_debug_dynamic;
+ productName = libdns_sd.dylib;
+ productReference = FFA572390AF18F1C0055A0F1 /* libsystem_dnssd_debug.dylib */;
+ productType = "com.apple.product-type.library.dynamic";
};
- FFA5723C0AF18F450055A0F1 /* libdns_sd profile */ = {
+ FFA5723C0AF18F450055A0F1 /* libdns_sd_profile_dynamic */ = {
isa = PBXNativeTarget;
- buildConfigurationList = FFA572430AF18F450055A0F1 /* Build configuration list for PBXNativeTarget "libdns_sd profile" */;
+ buildConfigurationList = FFA572430AF18F450055A0F1 /* Build configuration list for PBXNativeTarget "libdns_sd_profile_dynamic" */;
buildPhases = (
FFA5723D0AF18F450055A0F1 /* Headers */,
FFA5723E0AF18F450055A0F1 /* Sources */,
);
dependencies = (
);
- name = "libdns_sd profile";
- productName = libdns_sd.a;
- productReference = FFA572450AF18F450055A0F1 /* libdns_sd_profile.a */;
- productType = "com.apple.product-type.library.static";
+ name = libdns_sd_profile_dynamic;
+ productName = libdns_sd.dylib;
+ productReference = FFA572450AF18F450055A0F1 /* libsystem_dnssd_profile.dylib */;
+ productType = "com.apple.product-type.library.dynamic";
};
- FFB765830AEED9C700583A2C /* libdns_sd */ = {
+ FFB765830AEED9C700583A2C /* libdns_sd_dynamic */ = {
isa = PBXNativeTarget;
- buildConfigurationList = FFB765890AEED9FB00583A2C /* Build configuration list for PBXNativeTarget "libdns_sd" */;
+ buildConfigurationList = FFB765890AEED9FB00583A2C /* Build configuration list for PBXNativeTarget "libdns_sd_dynamic" */;
buildPhases = (
FFB765800AEED9C700583A2C /* Headers */,
FFB765810AEED9C700583A2C /* Sources */,
FFB765820AEED9C700583A2C /* Frameworks */,
- FFA572500AF190070055A0F1 /* CopyFiles */,
21DE714D115831CB00DD4BD1 /* ShellScript */,
);
buildRules = (
);
dependencies = (
);
- name = libdns_sd;
- productName = libdns_sd.a;
- productReference = FFB765840AEED9C700583A2C /* libdns_sd.a */;
- productType = "com.apple.product-type.library.static";
+ name = libdns_sd_dynamic;
+ productName = libdns_sd.dylib;
+ productReference = FFB765840AEED9C700583A2C /* libsystem_dnssd.dylib */;
+ productType = "com.apple.product-type.library.dynamic";
};
/* End PBXNativeTarget section */
isa = PBXProject;
buildConfigurationList = D284BE2B0ADD78180027CCDF /* Build configuration list for PBXProject "mDNSResponder" */;
compatibilityVersion = "Xcode 3.1";
+ developmentRegion = English;
hasScannedForEncodings = 1;
+ knownRegions = (
+ English,
+ Japanese,
+ French,
+ German,
+ );
mainGroup = 08FB7794FE84155DC02AAC07 /* mDNSResponder */;
projectDirPath = "";
projectRoot = "";
D284BEBF0ADD80A20027CCDF /* dnsextd */,
D284BEDB0ADD80A70027CCDF /* ddnswriteconfig */,
D284BEEA0ADD80B00027CCDF /* PreferencePane */,
- FFB765830AEED9C700583A2C /* libdns_sd */,
- FFA572300AF18F1C0055A0F1 /* libdns_sd debug */,
- FFA5723C0AF18F450055A0F1 /* libdns_sd profile */,
- FFA572650AF190F10055A0F1 /* SystemLibraries */,
+ FFB765830AEED9C700583A2C /* libdns_sd_dynamic */,
+ FFA572300AF18F1C0055A0F1 /* libdns_sd_debug_dynamic */,
+ FFA5723C0AF18F450055A0F1 /* libdns_sd_profile_dynamic */,
+ FFA572650AF190F10055A0F1 /* SystemLibrariesDynamic */,
+ 213FB21712028A7A002B3A08 /* BonjourEvents */,
+ 2141DCF8123FFB5D0086D23E /* SystemLibraries */,
+ 2141DD0B123FFC7F0086D23E /* SystemLibrariesStatic */,
+ 2141DD1C123FFCDB0086D23E /* libdns_sd_static */,
+ 2141DD23123FFD0F0086D23E /* libdns_sd_debug_static */,
+ 2141DD29123FFD2C0086D23E /* libdns_sd_profile_static */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
+ 213FB21412028A7A002B3A08 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
D284BEEE0ADD80B00027CCDF /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
shellPath = /bin/sh;
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";
};
+ 1F7B473C12B82BFD00868AEF /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/bash;
+ shellScript = "# check if we're building for the simulator and patch the \"id\" of the library (as reported by otool -D) to\n# be just /usr/lib/system/libsystem_sim_dnssd.dylib etc. Also remove the usr directory as it is not needed\n# for simulator\nif [ \"${RC_ProjectName%_Sim}\" != \"${RC_ProjectName}\" ] ; then\n\tif [ -d ${DSTROOT}${SDKROOT}/usr/lib/system ] ; then\n\t\tfor lib in ${DSTROOT}${SDKROOT}/usr/lib/system/*.dylib ; do\n\t\t\tinstall_name_tool -id \"${lib#${DSTROOT}${SDKROOT}}\" \"${lib}\"\n\t\tdone\n\tfi\n\trm -rf $DSTROOT/usr\nfi\n";
+ };
+ 2130256B12400DE600AC839F /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = "#if we are building for simulator, change the installation path\nif [ \"${RC_ProjectName%_Sim}\" != \"${RC_ProjectName}\" ] ; then\n\tDSTROOT=${DSTROOT}${SDKROOT}\nfi\nmkdir -p \"$DSTROOT/usr/include/DNSServiceDiscovery\"\ncp $SRCROOT/DNSServiceDiscovery.h $DSTROOT/usr/include/DNSServiceDiscovery\nsed 's/\\(^#define _DNS_SD_LIBDISPATCH \\)0$/\\1 1/' \"$SRCROOT/../mDNSShared/dns_sd.h\" > \"$DSTROOT/usr/include/dns_sd.h\"";
+ };
21DE714D115831CB00DD4BD1 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 8;
);
runOnlyForDeploymentPostprocessing = 1;
shellPath = /bin/sh;
- shellScript = "sed 's/\\(^#define _DNS_SD_LIBDISPATCH \\)0$/\\1 1/' \"$SRCROOT/../mDNSShared/dns_sd.h\" > \"$DSTROOT/usr/include/dns_sd.h\"";
+ shellScript = "#if we are building for simulator, change the installation path\nif [ \"${RC_ProjectName%_Sim}\" != \"${RC_ProjectName}\" ] ; then\n\tDSTROOT=${DSTROOT}${SDKROOT}\nfi\nmkdir -p \"$DSTROOT/usr/include/DNSServiceDiscovery\"\ncp $SRCROOT/DNSServiceDiscovery.h $DSTROOT/usr/include/DNSServiceDiscovery\nsed 's/\\(^#define _DNS_SD_LIBDISPATCH \\)0$/\\1 1/' \"$SRCROOT/../mDNSShared/dns_sd.h\" > \"$DSTROOT/usr/include/dns_sd.h\"";
};
D284BE510ADD80740027CCDF /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
);
runOnlyForDeploymentPostprocessing = 1;
shellPath = /bin/bash;
- 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";
+ 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\nif [ ! -z \"${IPHONEOS_DEPLOYMENT_TARGET}\" ] ; then\n SANDBOXDST=\"${DSTROOT}/usr/local/share/sandbox/profiles/embedded/builtin\"\nelse\n SANDBOXDST=\"${DSTROOT}/usr/share/sandbox\"\nfi\n(umask 022; mkdir -p -m 0755 \"$SANDBOXDST\")\ncp \"${SRCROOT}/mDNSResponder.sb\" \"${SANDBOXDST}/mDNSResponder.sb\"\n";
};
D284BE760ADD80800027CCDF /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
);
runOnlyForDeploymentPostprocessing = 1;
shellPath = /bin/sh;
- shellScript = "# Install plists to tell launchd how to start mDNSResponder and mDNSResponderHelper\n\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\nfi\n\nif [ ! -z \"${IPHONEOS_DEPLOYMENT_TARGET}\" ] ; then\nplutil -convert binary1 ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponder.plist\nplutil -convert binary1 ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponderHelper.plist\nfi\n\n\n# Install Sandbox profile\n\nif [ ! -z \"${IPHONEOS_DEPLOYMENT_TARGET}\" ] ; then\n SANDBOXDST=\"${DSTROOT}/usr/local/share/sandbox/profiles/embedded/builtin\"\nelse\n SANDBOXDST=\"${DSTROOT}/usr/share/sandbox\"\nfi\n\n(umask 022; mkdir -p -m 0755 \"$SANDBOXDST\")\n\nif [[ \"${MACOSX_DEPLOYMENT_TARGET}\" == 10.[0-6] ]] ; then\n sed '/iokit-open/d' \"${SRCROOT}/mDNSResponder.sb\" > \"${SANDBOXDST}/mDNSResponder.sb\"\nelse\n cp \"${SRCROOT}/mDNSResponder.sb\" \"${SANDBOXDST}/mDNSResponder.sb\"\nfi\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\nfi\n\nif [ ! -z \"${IPHONEOS_DEPLOYMENT_TARGET}\" ] ; then\nplutil -convert binary1 ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponder.plist\nplutil -convert binary1 ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponderHelper.plist\nfi\n";
};
FF37FAAD0BC581780044A5CF /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
+ 213FB21512028A7A002B3A08 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 213FB23C12028C4A002B3A08 /* BonjourEvents.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 2141DD1A123FFCDB0086D23E /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 215FFAEE124000F900470DE1 /* dnssd_ipc.c in Sources */,
+ 215FFAEF124000F900470DE1 /* dnssd_clientlib.c in Sources */,
+ 215FFAF0124000F900470DE1 /* dnssd_clientstub.c in Sources */,
+ 215FFAF1124000F900470DE1 /* DNSServiceDiscovery.c in Sources */,
+ 215FFAF2124000F900470DE1 /* DNSServiceDiscoveryRequest.defs in Sources */,
+ 215FFAF3124000F900470DE1 /* DNSServiceDiscoveryReply.defs in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 2141DD21123FFD0F0086D23E /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 215FFAF41240011800470DE1 /* dnssd_ipc.c in Sources */,
+ 215FFAF51240011800470DE1 /* dnssd_clientlib.c in Sources */,
+ 215FFAF61240011800470DE1 /* dnssd_clientstub.c in Sources */,
+ 215FFAF71240011800470DE1 /* DNSServiceDiscovery.c in Sources */,
+ 215FFAF81240011800470DE1 /* DNSServiceDiscoveryRequest.defs in Sources */,
+ 215FFAF91240011800470DE1 /* DNSServiceDiscoveryReply.defs in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 2141DD27123FFD2C0086D23E /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 215FFAFA1240013400470DE1 /* dnssd_ipc.c in Sources */,
+ 215FFAFB1240013400470DE1 /* dnssd_clientlib.c in Sources */,
+ 215FFAFC1240013400470DE1 /* dnssd_clientstub.c in Sources */,
+ 215FFAFD1240013400470DE1 /* DNSServiceDiscovery.c in Sources */,
+ 215FFAFE1240013400470DE1 /* DNSServiceDiscoveryRequest.defs in Sources */,
+ 215FFAFF1240013400470DE1 /* DNSServiceDiscoveryReply.defs in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
2E0405ED0C31955500F13B59 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
2E96A51D0C39BDAC0087C4D2 /* helper-main.c in Sources */,
2E8165E90C5980EE00485EB2 /* pfkey.c in Sources */,
4AE9B04B0F39448B0080B59D /* safe_vproc.c in Sources */,
+ 4BD2B63A134FE09F002B96D5 /* P2PPacketFilter.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
target = 03067D640C83A3700022BE1F /* Build Some */;
targetProxy = 03067D850C849CC30022BE1F /* PBXContainerItemProxy */;
};
+ 2130257112400E9300AC839F /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = FFA572650AF190F10055A0F1 /* SystemLibrariesDynamic */;
+ targetProxy = 2130257012400E9300AC839F /* PBXContainerItemProxy */;
+ };
+ 2141DCFD123FFB7D0086D23E /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 2141DCF8123FFB5D0086D23E /* SystemLibraries */;
+ targetProxy = 2141DCFC123FFB7D0086D23E /* PBXContainerItemProxy */;
+ };
+ 2141DD0E123FFC960086D23E /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 2141DD0B123FFC7F0086D23E /* SystemLibrariesStatic */;
+ targetProxy = 2141DD0D123FFC960086D23E /* PBXContainerItemProxy */;
+ };
+ 215FFB19124002C100470DE1 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 2141DD1C123FFCDB0086D23E /* libdns_sd_static */;
+ targetProxy = 215FFB18124002C100470DE1 /* PBXContainerItemProxy */;
+ };
+ 215FFB1B124002C700470DE1 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 2141DD23123FFD0F0086D23E /* libdns_sd_debug_static */;
+ targetProxy = 215FFB1A124002C700470DE1 /* PBXContainerItemProxy */;
+ };
+ 215FFB1D124002CC00470DE1 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 2141DD29123FFD2C0086D23E /* libdns_sd_profile_static */;
+ targetProxy = 215FFB1C124002CC00470DE1 /* PBXContainerItemProxy */;
+ };
+ 216EF8EC120295AA004917D2 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 213FB21712028A7A002B3A08 /* BonjourEvents */;
+ targetProxy = 216EF8EB120295AA004917D2 /* PBXContainerItemProxy */;
+ };
4AE4716A0EAFF83800A6C5AD /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 4AE471670EAFF81900A6C5AD /* dns_sd.jar */;
};
FFA572690AF190FF0055A0F1 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
- target = FFB765830AEED9C700583A2C /* libdns_sd */;
+ target = FFB765830AEED9C700583A2C /* libdns_sd_dynamic */;
targetProxy = FFA572680AF190FF0055A0F1 /* PBXContainerItemProxy */;
};
FFA5726B0AF191010055A0F1 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
- target = FFA572300AF18F1C0055A0F1 /* libdns_sd debug */;
+ target = FFA572300AF18F1C0055A0F1 /* libdns_sd_debug_dynamic */;
targetProxy = FFA5726A0AF191010055A0F1 /* PBXContainerItemProxy */;
};
FFA5726D0AF191020055A0F1 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
- target = FFA5723C0AF18F450055A0F1 /* libdns_sd profile */;
+ target = FFA5723C0AF18F450055A0F1 /* libdns_sd_profile_dynamic */;
targetProxy = FFA5726C0AF191020055A0F1 /* PBXContainerItemProxy */;
};
- FFA572710AF191230055A0F1 /* PBXTargetDependency */ = {
- isa = PBXTargetDependency;
- target = FFA572650AF190F10055A0F1 /* SystemLibraries */;
- targetProxy = FFA572700AF191230055A0F1 /* PBXContainerItemProxy */;
- };
FFAE66F9105F0CF100162116 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = D284BEDB0ADD80A70027CCDF /* ddnswriteconfig */;
};
name = Development;
};
+ 213FB21A12028A7B002B3A08 /* Development */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ BUNDLE_LOADER = /usr/libexec/UserEventAgent;
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_MODEL_TUNING = G5;
+ INFOPLIST_FILE = "BonjourEvents-Info.plist";
+ INSTALL_PATH = /System/Library/UserEventPlugins/;
+ PREBINDING = NO;
+ PRODUCT_NAME = BonjourEvents;
+ WRAPPER_EXTENSION = plugin;
+ };
+ name = Development;
+ };
+ 2141DCF9123FFB5D0086D23E /* Development */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ PRODUCT_NAME = SystemLibraries;
+ };
+ name = Development;
+ };
+ 2141DD0C123FFC7F0086D23E /* Development */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ PRODUCT_NAME = SystemLibrariesStatic;
+ };
+ name = Development;
+ };
+ 2141DD1E123FFCDB0086D23E /* Development */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
+ CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "$(inherited)",
+ "__DARWIN_NON_CANCELABLE=1",
+ );
+ HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/";
+ INSTALLHDRS_COPY_PHASE = YES;
+ INSTALLHDRS_SCRIPT_PHASE = YES;
+ INSTALL_PATH = /usr/local/lib/system;
+ PREBINDING = NO;
+ PRODUCT_NAME = dns_sd;
+ };
+ name = Development;
+ };
+ 2141DD25123FFD100086D23E /* Development */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
+ CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "$(inherited)",
+ "__DARWIN_NON_CANCELABLE=1",
+ );
+ HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/";
+ INSTALL_PATH = /usr/local/lib/system;
+ PREBINDING = NO;
+ PRODUCT_NAME = dns_sd_debug;
+ };
+ name = Development;
+ };
+ 2141DD2B123FFD2C0086D23E /* Development */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
+ CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "$(inherited)",
+ "__DARWIN_NON_CANCELABLE=1",
+ );
+ HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/";
+ INSTALL_PATH = /usr/local/lib/system;
+ PREBINDING = NO;
+ PRODUCT_NAME = dns_sd_profile;
+ };
+ name = Development;
+ };
2E0405F20C31955500F13B59 /* Development */ = {
isa = XCBuildConfiguration;
buildSettings = {
CONFIGURATION_TEMP_DIR = "$(PROJECT_TEMP_DIR)";
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
- GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_WARN_CHECK_SWITCH_STATEMENTS = NO;
- HEADER_SEARCH_PATHS = "${CONFIGURATION_TEMP_DIR}";
+ HEADER_SEARCH_PATHS = (
+ "${CONFIGURATION_TEMP_DIR}",
+ "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders",
+ );
INSTALL_PATH = /usr/sbin;
LD_MAP_FILE_PATH = "$(TARGET_TEMP_DIR)/$(PRODUCT_NAME)-LinkMap-$(CURRENT_VARIANT)-$(CURRENT_ARCH).txt";
LIBRARY_SEARCH_PATHS = "\"${CONFIGURATION_TEMP_DIR}\"";
"$(inherited)",
"-lipsec",
);
+ "OTHER_LDFLAGS[sdk=iphoneos*] [arch=*]" = "-lipsec -Wl,-pie";
+ "OTHER_LDFLAGS[sdk=macosx10.7][arch=*]" = (
+ "-lipsec",
+ "-Wl,-pie",
+ );
PREBINDING = NO;
PRODUCT_NAME = mDNSResponderHelper;
};
CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
GCC_DYNAMIC_NO_PIC = NO;
- GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
OTHER_CFLAGS = "";
OTHER_LDFLAGS = "";
CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)";
DEAD_CODE_STRIPPING = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
+ "__APPLE_USE_RFC_3542=1",
"_DNS_SD_LIBDISPATCH=1",
"APPLE_OSX_mDNSResponder=1",
"__MigTypeCheck=1",
CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
- "\"$(SDKROOT)/$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks\"",
+ "$(SDKROOT)/$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
);
GCC_DYNAMIC_NO_PIC = NO;
- GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
HEADER_SEARCH_PATHS = (
../mDNSShared,
);
OTHER_LDFLAGS = "";
"OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-Wl,-pie",
"-weak_framework",
DeviceToDeviceManager,
);
"OTHER_LDFLAGS[sdk=macosx10.6][arch=*]" = "-lAWACS";
"OTHER_LDFLAGS[sdk=macosx10.7][arch=*]" = (
+ "-Wl,-pie",
"-lAWACS",
"-weak_framework",
WebFilterDNS,
CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
- "\"$(SDKROOT)/$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks\"",
+ "$(SDKROOT)/$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
);
GCC_DYNAMIC_NO_PIC = NO;
- GCC_ENABLE_FIX_AND_CONTINUE = YES;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
);
OTHER_LDFLAGS = "";
"OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+ "-Wl,-pie",
"-weak_framework",
DeviceToDeviceManager,
);
"OTHER_LDFLAGS[sdk=macosx10.6][arch=*]" = "-lAWACS";
"OTHER_LDFLAGS[sdk=macosx10.7][arch=*]" = (
+ "-Wl,-pie",
"-lAWACS",
"-weak_framework",
WebFilterDNS,
__text,
mDNSResponder.order,
);
+ SKIP_INSTALL = YES;
};
name = Development;
};
CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
GCC_DYNAMIC_NO_PIC = NO;
- GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
INSTALL_PATH = /usr/bin;
OTHER_CFLAGS = "-no-cpp-precomp";
CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
GCC_DYNAMIC_NO_PIC = NO;
- GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
HEADER_SEARCH_PATHS = ../mDNSShared;
INSTALL_PATH = /usr/bin;
DYLIB_CURRENT_VERSION = 1;
EXECUTABLE_EXTENSION = jnilib;
GCC_DYNAMIC_NO_PIC = NO;
- GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
HEADER_SEARCH_PATHS = (
CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
FRAMEWORK_SEARCH_PATHS = "";
GCC_DYNAMIC_NO_PIC = NO;
- GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
HEADER_SEARCH_PATHS = (
"${APPLE_INTERNAL_DEVELOPER_DIR}/Headers",
LEXFLAGS = "";
LEX_CASE_INSENSITIVE_SCANNER = YES;
LIBRARY_SEARCH_PATHS = "\"${CONFIGURATION_TEMP_DIR}\"";
- MACOSX_DEPLOYMENT_TARGET = 10.4;
+ MACOSX_DEPLOYMENT_TARGET = 10.5;
OTHER_CFLAGS = (
"-no-cpp-precomp",
"-UAPPLE_OSX_mDNSResponder",
);
OTHER_LDFLAGS = "";
+ "OTHER_LDFLAGS[sdk=macosx10.7][arch=*]" = "-Wl,-pie";
OTHER_REZFLAGS = "";
PRODUCT_NAME = dnsextd;
REZ_EXECUTABLE = YES;
CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
GCC_DYNAMIC_NO_PIC = NO;
- GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
INSTALL_PATH = "/Library/Application Support/Bonjour";
MACOSX_DEPLOYMENT_TARGET = 10.5;
CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
EXPORTED_SYMBOLS_FILE = "";
GCC_DYNAMIC_NO_PIC = NO;
- GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_ENABLE_OBJC_GC = supported;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
COPY_PHASE_STRIP = NO;
+ EXECUTABLE_EXTENSION = dylib;
GCC_DYNAMIC_NO_PIC = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"__DARWIN_NON_CANCELABLE=1",
);
HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/";
- INSTALL_PATH = /usr/local/lib/system;
- PRODUCT_NAME = dns_sd_debug;
+ INSTALL_PATH = /usr/lib/system;
+ "INSTALL_PATH[sdk=iphonesimulator*]" = "$(SDKROOT)/usr/lib/system";
+ OTHER_LDFLAGS = (
+ "-umbrella",
+ System,
+ );
+ "OTHER_LDFLAGS[sdk=iphonesimulator*][arch=*]" = "";
+ PRODUCT_NAME = libsystem_dnssd_debug;
+ "PRODUCT_NAME[sdk=iphonesimulator*]" = libsystem_sim_dnssd_debug;
};
name = Development;
};
CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
COPY_PHASE_STRIP = NO;
+ EXECUTABLE_EXTENSION = dylib;
GCC_DYNAMIC_NO_PIC = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
);
GENERATE_PROFILING_CODE = YES;
HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/";
- INSTALL_PATH = /usr/local/lib/system;
- PRODUCT_NAME = dns_sd_profile;
+ INSTALL_PATH = /usr/lib/system;
+ "INSTALL_PATH[sdk=iphonesimulator*]" = "$(SDKROOT)/usr/lib/system";
+ OTHER_LDFLAGS = (
+ "-umbrella",
+ System,
+ );
+ "OTHER_LDFLAGS[sdk=iphonesimulator*][arch=*]" = "";
+ PRODUCT_NAME = libsystem_dnssd_profile;
+ "PRODUCT_NAME[sdk=iphonesimulator*]" = libsystem_sim_dnssd_profile;
};
name = Development;
};
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
- PRODUCT_NAME = SystemLibraries;
+ PRODUCT_NAME = SystemLibrariesDynamic;
};
name = Development;
};
CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
COPY_PHASE_STRIP = NO;
+ EXECUTABLE_EXTENSION = dylib;
GCC_DYNAMIC_NO_PIC = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/";
INSTALLHDRS_COPY_PHASE = YES;
INSTALLHDRS_SCRIPT_PHASE = YES;
- INSTALL_PATH = /usr/local/lib/system;
- PRODUCT_NAME = dns_sd;
+ INSTALL_PATH = /usr/lib/system;
+ "INSTALL_PATH[sdk=iphonesimulator*]" = "$(SDKROOT)/usr/lib/system";
+ OTHER_LDFLAGS = (
+ "-umbrella",
+ System,
+ );
+ "OTHER_LDFLAGS[sdk=iphonesimulator*][arch=*]" = "";
+ PRODUCT_NAME = libsystem_dnssd;
+ "PRODUCT_NAME[sdk=iphonesimulator*]" = libsystem_sim_dnssd;
};
name = Development;
};
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Development;
};
+ 213FB21B12028A7C002B3A08 /* Build configuration list for PBXNativeTarget "BonjourEvents" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 213FB21A12028A7B002B3A08 /* Development */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Development;
+ };
+ 2141DD08123FFB830086D23E /* Build configuration list for PBXAggregateTarget "SystemLibraries" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 2141DCF9123FFB5D0086D23E /* Development */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Development;
+ };
+ 2141DD18123FFC990086D23E /* Build configuration list for PBXAggregateTarget "SystemLibrariesStatic" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 2141DD0C123FFC7F0086D23E /* Development */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Development;
+ };
+ 2141DD1F123FFCF90086D23E /* Build configuration list for PBXNativeTarget "libdns_sd_static" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 2141DD1E123FFCDB0086D23E /* Development */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Development;
+ };
+ 2141DD35123FFD3B0086D23E /* Build configuration list for PBXNativeTarget "libdns_sd_debug_static" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 2141DD25123FFD100086D23E /* Development */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Development;
+ };
+ 2141DD36123FFD3B0086D23E /* Build configuration list for PBXNativeTarget "libdns_sd_profile_static" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 2141DD2B123FFD2C0086D23E /* Development */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Development;
+ };
2E0405F30C31956600F13B59 /* Build configuration list for PBXNativeTarget "mDNSResponderHelper" */ = {
isa = XCConfigurationList;
buildConfigurations = (
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Development;
};
- FFA572370AF18F1C0055A0F1 /* Build configuration list for PBXNativeTarget "libdns_sd debug" */ = {
+ FFA572370AF18F1C0055A0F1 /* Build configuration list for PBXNativeTarget "libdns_sd_debug_dynamic" */ = {
isa = XCConfigurationList;
buildConfigurations = (
FFA572380AF18F1C0055A0F1 /* Development */,
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Development;
};
- FFA572430AF18F450055A0F1 /* Build configuration list for PBXNativeTarget "libdns_sd profile" */ = {
+ FFA572430AF18F450055A0F1 /* Build configuration list for PBXNativeTarget "libdns_sd_profile_dynamic" */ = {
isa = XCConfigurationList;
buildConfigurations = (
FFA572440AF18F450055A0F1 /* Development */,
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Development;
};
- FFA5726E0AF191200055A0F1 /* Build configuration list for PBXAggregateTarget "SystemLibraries" */ = {
+ FFA5726E0AF191200055A0F1 /* Build configuration list for PBXAggregateTarget "SystemLibrariesDynamic" */ = {
isa = XCConfigurationList;
buildConfigurations = (
FFA5726F0AF191200055A0F1 /* Development */,
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Development;
};
- FFB765890AEED9FB00583A2C /* Build configuration list for PBXNativeTarget "libdns_sd" */ = {
+ FFB765890AEED9FB00583A2C /* Build configuration list for PBXNativeTarget "libdns_sd_dynamic" */ = {
isa = XCConfigurationList;
buildConfigurations = (
FFB7658A0AEED9FB00583A2C /* Development */,
q->ForceMCast = mDNStrue; // Query via multicast, even for apparently uDNS names like 1.1.1.17.in-addr.arpa.
q->ReturnIntermed = mDNStrue;
q->SuppressUnusable = mDNSfalse;
- q->WakeOnResolve = mDNSfalse;
+ q->SearchListIndex = 0;
+ q->AppendSearchDomains = 0;
+ q->RetryWithSearchDomains = mDNSfalse;
+ q->TimeoutQuestion = 0;
+ q->WakeOnResolve = 0;
+ q->qnameOrig = mDNSNULL;
q->QuestionCallback = callback;
q->QuestionContext = NULL;
# we get build failures: ‘daemon’ is deprecated (declared at /usr/include/stdlib.h:283)
CFLAGS_OS = -DHAVE_IPV6 -no-cpp-precomp -Werror -Wdeclaration-after-statement \
-D__MAC_OS_X_VERSION_MIN_REQUIRED=__MAC_OS_X_VERSION_10_4 #-Wunreachable-code
-CC = @gcc-4.0
+CC = gcc
LD = $(CC) -dynamiclib
LINKOPTS = -lSystem
LDSUFFIX = dylib
struct timeval tv;
struct tm tm;
- const mDNSu32 index = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID);
+ const mDNSu32 index = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNSfalse);
char if_name[IFNAMSIZ]; // Older Linux distributions don't define IF_NAMESIZE
if_indextoname(index, if_name);
gettimeofday(&tv, NULL);
// For now we're only interested in monitoring IPv4 traffic.
// All IPv6 packets should just be duplicates of the v4 packets.
- if (!goodinterface) goodinterface = (FilterInterface == (int)mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID));
+ if (!goodinterface) goodinterface = (FilterInterface == (int)mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNSfalse));
if (goodinterface && AddressMatchesFilterList(srcaddr))
{
mDNS_Lock(m);
gStopEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (gStopEvent == INVALID_HANDLE_VALUE) return mStatus_UnknownErr;
if (!SetConsoleCtrlHandler(ConsoleControlHandler, TRUE)) return mStatus_UnknownErr;
- SetSocketEventsEnabled(&mDNSStorage, TRUE);
- while (WaitForSingleObjectEx(gStopEvent, INFINITE, TRUE) == WAIT_IO_COMPLETION);
- SetSocketEventsEnabled(&mDNSStorage, FALSE);
+ while (WaitForSingleObjectEx(gStopEvent, INFINITE, TRUE) == WAIT_IO_COMPLETION)
+ DispatchSocketEvents(&mDNSStorage);
if (!SetConsoleCtrlHandler(ConsoleControlHandler, FALSE)) return mStatus_UnknownErr;
CloseHandle(gStopEvent);
#else
{
char buffer[32];
- mDNS_SetupResourceRecord(&p->RR_A, mDNSNULL, mDNSInterface_Any, kDNSType_A, 60, kDNSRecordTypeUnique, HostNameCallback, p);
- mDNS_SetupResourceRecord(&p->RR_PTR, mDNSNULL, mDNSInterface_Any, kDNSType_PTR, 60, kDNSRecordTypeKnownUnique, HostNameCallback, p);
+ mDNS_SetupResourceRecord(&p->RR_A, mDNSNULL, mDNSInterface_Any, kDNSType_A, 60, kDNSRecordTypeUnique, AuthRecordAny, HostNameCallback, p);
+ mDNS_SetupResourceRecord(&p->RR_PTR, mDNSNULL, mDNSInterface_Any, kDNSType_PTR, 60, kDNSRecordTypeKnownUnique, AuthRecordAny, HostNameCallback, p);
p->RR_A.namestorage.c[0] = 0;
AppendDomainLabel(&p->RR_A.namestorage, &p->hostlabel);
txtbuffer, bptr-txtbuffer, // TXT data, length
mDNSNULL, 0, // Subtypes
mDNSInterface_Any, // Interface ID
- ServiceCallback, mDNSNULL); // Callback and context
+ ServiceCallback, mDNSNULL, 0); // Callback, context, flags
ConvertDomainNameToCString(recordset->RR_SRV.resrec.name, buffer);
printf("Made Service Records for %s\n", buffer);
ConvertDomainNameToCString(rr->resrec.name, buffer1);
DeconstructServiceName(rr->resrec.name, &n, &t, &d);
IncrementLabelSuffix(&n, mDNStrue);
- mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, mDNSNULL);
+ mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, mDNSNULL, mDNSfalse);
ConvertDomainNameToCString(rr->resrec.name, buffer2);
printf("Name Conflict! %s renamed as %s\n", buffer1, buffer2);
}
MakeDomainLabelFromLiteralString(&n, name);
MakeDomainNameFromDNSNameString(&t, type);
MakeDomainNameFromDNSNameString(&d, domain);
- mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, proxyhostname);
+ mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, proxyhostname, mDNSfalse);
ConvertDomainNameToCString(rr->resrec.name, buffer);
printf("Made Non-existence Record for %s\n", buffer);
}
text, textLen, // TXT data, length
NULL, 0, // Subtypes
mDNSInterface_Any, // Interface ID
- RegistrationCallback, thisServ); // Callback and context
+ RegistrationCallback, thisServ, 0); // Callback, context, flags
}
if (status == mStatus_NoError) {
thisServ->serviceID = gServiceID;
{
}
+mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason)
+ {
+ (void) m;
+ (void) allowSleep;
+ (void) reason;
+ }
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - /etc/hosts support
+#endif
+
+mDNSexport void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result)
+ {
+ (void)m; // unused
+ (void)rr;
+ (void)result;
+ }
+
+
#if COMPILER_LIKES_PRAGMA_MARK
#pragma mark ***** DDNS Config Platform Functions
#endif
mDNSAddr DNSAddr;
DNSAddr.type = mDNSAddrType_IPv4;
DNSAddr.ip.v4.NotAnInteger = ina.s_addr;
- mDNS_AddDNSServer(m, NULL, mDNSInterface_Any, &DNSAddr, UnicastDNSPort, mDNSfalse);
+ mDNS_AddDNSServer(m, NULL, mDNSInterface_Any, &DNSAddr, UnicastDNSPort, mDNSfalse, 0);
numOfServers++;
}
}
return (mDNSInterfaceID) intf;
}
-mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id)
+mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id, mDNSBool suppressNetworkChange)
{
PosixNetworkInterface *intf;
+ (void) suppressNetworkChange; // Unused
assert(m != NULL);
(void) iteration;
}
+mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf)
+ {
+ (void) rr;
+ (void) intf;
+
+ return 1;
+ }
+
mDNSlocal void mDNSPosixAddToFDSet(int *nfds, fd_set *readfds, int s)
{
if (*nfds < s + 1) *nfds = s + 1;
EndProject\r
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ControlPanelRes", "mDNSWindows\ControlPanel\ControlPanelRes.vcproj", "{5254AA9C-3D2E-4539-86D9-5EB0F4151215}"\r
EndProject\r
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ControlPanel", "mDNSWindows\ControlPanel\ControlPanel.vcproj", "{F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}"\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ControlPanel", "mDNSWindows\ControlPanel\ControlPanel.vcproj", "{0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}"\r
ProjectSection(ProjectDependencies) = postProject\r
{3A2B6325-3053-4236-84BD-AA9BE2E323E5} = {3A2B6325-3053-4236-84BD-AA9BE2E323E5}\r
EndProjectSection\r
{5254AA9C-3D2E-4539-86D9-5EB0F4151215}.Release|Win32.Build.0 = Release|Win32\r
{5254AA9C-3D2E-4539-86D9-5EB0F4151215}.Release|x64.ActiveCfg = Release|x64\r
{5254AA9C-3D2E-4539-86D9-5EB0F4151215}.Release|x64.Build.0 = Release|x64\r
- {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Debug|Any CPU.ActiveCfg = Debug|x64\r
- {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Debug|Mixed Platforms.ActiveCfg = Debug|x64\r
- {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Debug|Mixed Platforms.Build.0 = Debug|x64\r
- {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Debug|Win32.ActiveCfg = Debug|Win32\r
- {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Debug|Win32.Build.0 = Debug|Win32\r
- {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Debug|x64.ActiveCfg = Debug|x64\r
- {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Debug|x64.Build.0 = Debug|x64\r
- {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Release|Any CPU.ActiveCfg = Release|x64\r
- {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Release|Mixed Platforms.ActiveCfg = Release|x64\r
- {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Release|Mixed Platforms.Build.0 = Release|x64\r
- {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Release|Win32.ActiveCfg = Release|Win32\r
- {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Release|Win32.Build.0 = Release|Win32\r
- {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Release|x64.ActiveCfg = Release|x64\r
- {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Release|x64.Build.0 = Release|x64\r
+ {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|Any CPU.ActiveCfg = Debug|x64\r
+ {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|Mixed Platforms.ActiveCfg = Debug|x64\r
+ {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|Mixed Platforms.Build.0 = Debug|x64\r
+ {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|Win32.ActiveCfg = Debug|Win32\r
+ {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|Win32.Build.0 = Debug|Win32\r
+ {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|x64.ActiveCfg = Debug|x64\r
+ {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|x64.Build.0 = Debug|x64\r
+ {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|Any CPU.ActiveCfg = Release|x64\r
+ {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|Mixed Platforms.ActiveCfg = Release|x64\r
+ {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|Mixed Platforms.Build.0 = Release|x64\r
+ {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|Win32.ActiveCfg = Release|Win32\r
+ {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|Win32.Build.0 = Release|Win32\r
+ {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|x64.ActiveCfg = Release|x64\r
+ {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|x64.Build.0 = Release|x64\r
{7826EA27-D4CC-4FAA-AD23-DF813823227B}.Debug|Any CPU.ActiveCfg = Debug|Win32\r
{7826EA27-D4CC-4FAA-AD23-DF813823227B}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32\r
{7826EA27-D4CC-4FAA-AD23-DF813823227B}.Debug|Mixed Platforms.Build.0 = Debug|Win32\r
{
DomainAuthInfo *info = (DomainAuthInfo*)mDNSPlatformMemAllocate(sizeof(*info));
// for now we assume keyname = service reg domain and we use same key for service and hostname registration
- err = mDNS_SetSecretForDomain(m, info, domain, domain, buf, mDNSfalse);
+ err = mDNS_SetSecretForDomain(m, info, domain, domain, buf, NULL, 0, NULL);
if (err) LogMsg("ERROR: mDNS_SetSecretForDomain returned %d for domain %##s", err, domain->c);
}
*/
#ifndef _DNS_SD_H
-#define _DNS_SD_H 2582100
+#define _DNS_SD_H 3200500
#ifdef __cplusplus
extern "C" {
* if this host has no routable IPv4 address, the call will not try to look up IPv4 addresses for
* "hostname".
*/
+
+ kDNSServiceFlagsTimeout = 0x10000,
+ /*
+ * When kDNServiceFlagsTimeout is passed to DNSServiceQueryRecord or DNSServiceGetAddrInfo, the query is
+ * stopped after a certain number of seconds have elapsed. The time at which the query will be stopped
+ * is determined by the system and cannot be configured by the user. The query will be stopped irrespective
+ * of whether a response was given earlier or not. When the query is stopped, the callback will be called
+ * with an error code of kDNSServiceErr_Timeout and a NULL sockaddr will be returned for DNSServiceGetAddrInfo
+ * and zero length rdata will be returned for DNSServiceQueryRecord.
+ */
+
+ kDNSServiceFlagsIncludeP2P = 0x20000,
+ /*
+ * Include P2P interfaces when kDNSServiceInterfaceIndexAny is specified.
+ * By default, specifying kDNSServiceInterfaceIndexAny does not include P2P interfaces.
+ */
kDNSServiceFlagsWakeOnResolve = 0x40000
/*
* This flag is meaningful only in DNSServiceResolve. When set, it tries to send a magic packet
* to wake up the client.
*/
-
};
/* Possible protocols for DNSServiceNATPortMappingCreate(). */
kDNSServiceErr_NATPortMappingUnsupported = -65564, /* NAT doesn't support NAT-PMP or UPnP */
kDNSServiceErr_NATPortMappingDisabled = -65565, /* NAT supports NAT-PMP or UPnP but it's disabled by the administrator */
kDNSServiceErr_NoRouter = -65566, /* No router currently configured (probably no network connectivity) */
- kDNSServiceErr_PollingMode = -65567
+ kDNSServiceErr_PollingMode = -65567,
+ kDNSServiceErr_Timeout = -65568
/* mDNS Error codes are in the range
* FFFE FF00 (-65792) to FFFE FFFF (-65537) */
* interface via which the service can be accessed.
*
* If applications pass kDNSServiceInterfaceIndexAny to DNSServiceBrowse
- * or DNSServiceQueryRecord, the operation will also include P2P. In this
- * case, if a service instance or the record being queried is found over P2P,
- * the resulting ADD event will indicate kDNSServiceInterfaceIndexP2P as the
- * interface index.
+ * or DNSServiceQueryRecord, they must set the kDNSServiceFlagsIncludeP2P flag
+ * to include P2P. In this case, if a service instance or the record being queried
+ * is found over P2P, the resulting ADD event will indicate kDNSServiceInterfaceIndexP2P
+ * as the interface index.
*/
#define kDNSServiceInterfaceIndexAny 0
( void ) d;
- mDNS_SetupResourceRecord(&rr, NULL, 0, kDNSType_SRV, SRV_TTL, kDNSRecordTypeUnique, NULL, NULL);
+ mDNS_SetupResourceRecord(&rr, NULL, 0, kDNSType_SRV, SRV_TTL, kDNSRecordTypeUnique, AuthRecordAny, NULL, NULL);
rr.resrec.rrclass = kDNSClass_IN;
rr.resrec.rdata->u.srv.priority = 0;
rr.resrec.rdata->u.srv.weight = 0;
mDNSlocal void FormatLLQOpt(AuthRecord *opt, int opcode, const mDNSOpaque64 *const id, mDNSs32 lease)
{
mDNSPlatformMemZero(opt, sizeof(*opt));
- mDNS_SetupResourceRecord(opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
+ mDNS_SetupResourceRecord(opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
opt->resrec.rrclass = NormalMaxDNSMessageData;
opt->resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record
opt->resrec.rdestimate = sizeof(rdataOPT);
const mDNSAddr *const srcaddr, const mDNSIPPort srcport,
const mDNSAddr *const dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID iid)
{ ( void ) m; ( void ) msg; ( void ) end; ( void ) srcaddr; ( void ) srcport; ( void ) dstaddr; ( void ) dstport; ( void ) iid; }
-DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped)
- { ( void ) m; ( void ) d; ( void ) interface; ( void ) addr; ( void ) port; ( void ) scoped; return(NULL); }
-void mDNS_AddSearchDomain(const domainname *const domain) { (void)domain; }
+DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout)
+ { ( void ) m; ( void ) d; ( void ) interface; ( void ) addr; ( void ) port; ( void ) scoped; ( void ) timeout; return(NULL); }
+void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID) { (void)domain; (void) InterfaceID;}
void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext)
{ ( void ) m; ( void ) fqdn; ( void ) StatusCallback; ( void ) StatusContext; }
mDNSs32 mDNS_Execute (mDNS *const m) { ( void ) m; return 0; }
{ ( void ) m; ( void ) v4addr; ( void ) v6addr; ( void ) router; }
mStatus uDNS_SetupDNSConfig( mDNS *const m ) { ( void ) m; return 0; }
mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info,
- const domainname *domain, const domainname *keyname, const char *b64keydata, mDNSBool AutoTunnel)
- { ( void ) m; ( void ) info; ( void ) domain; ( void ) keyname; ( void ) b64keydata; ( void ) AutoTunnel; return 0; }
+ const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, const char *autoTunnelPrefix)
+ { ( void ) m; ( void ) info; ( void ) domain; ( void ) keyname; ( void ) b64keydata; ( void ) hostname; (void) port; ( void ) autoTunnelPrefix; return 0; }
mStatus mDNS_StopQuery(mDNS *const m, DNSQuestion *const question) { ( void ) m; ( void ) question; return 0; }
void TriggerEventCompletion(void);
void TriggerEventCompletion() {}
txtRecord, txtLen, // TXT data, length
SubTypes, NumSubTypes, // Subtypes
mDNSInterface_Any, // Interface ID
- RegCallback, x); // Callback and context
+ RegCallback, x, 0); // Callback, context, flags
if (err) { mDNSPlatformMemFree(x); errormsg = "mDNS_RegisterService"; goto fail; }
// Succeeded: Wrap up and return
x->qSRV.ExpectUnique = mDNStrue;
x->qSRV.ForceMCast = mDNSfalse;
x->qSRV.ReturnIntermed = mDNSfalse;
+ x->qSRV.SuppressUnusable = mDNSfalse;
+ x->qSRV.SearchListIndex = 0;
+ x->qSRV.AppendSearchDomains = 0;
+ x->qSRV.RetryWithSearchDomains = mDNSfalse;
+ x->qSRV.TimeoutQuestion = 0;
+ x->qSRV.WakeOnResolve = 0;
+ x->qSRV.qnameOrig = mDNSNULL;
x->qSRV.QuestionCallback = FoundServiceInfo;
x->qSRV.QuestionContext = x;
x->qTXT.ForceMCast = mDNSfalse;
x->qTXT.ReturnIntermed = mDNSfalse;
x->qTXT.SuppressUnusable = mDNSfalse;
- x->qTXT.WakeOnResolve = mDNSfalse;
+ x->qTXT.SearchListIndex = 0;
+ x->qTXT.AppendSearchDomains = 0;
+ x->qTXT.RetryWithSearchDomains = mDNSfalse;
+ x->qTXT.TimeoutQuestion = 0;
+ x->qTXT.WakeOnResolve = 0;
+ x->qTXT.qnameOrig = mDNSNULL;
x->qTXT.QuestionCallback = FoundServiceInfo;
x->qTXT.QuestionContext = x;
x->q.ForceMCast = (flags & kDNSServiceFlagsForceMulticast) != 0;
x->q.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
x->q.SuppressUnsable = (flags & kDNSServiceFlagsSuppressUnusable) != 0;
+ x->q.SearchListIndex = 0;
+ x->q.AppendSearchDomains = 0;
+ x->q.RetryWithSearchDomains = mDNSfalse;
+ x->q.WakeOnResolve = 0;
+ x->q.qnameOrig = mDNSNULL;
x->q.QuestionCallback = DNSServiceQueryRecordResponse;
x->q.QuestionContext = x;
#include "dnssd_ipc.h"
+static int gDaemonErr = kDNSServiceErr_NoError;
+
#if defined(_WIN32)
#define _SSIZE_T
static int g_initWinsock = 0;
#define LOG_WARNING kDebugLevelWarning
+ #define LOG_INFO kDebugLevelInfo
static void syslog( int priority, const char * message, ...)
{
va_list args;
// Uncomment the line below to use the old error return mechanism of creating a temporary named socket (e.g. in /var/tmp)
//#define USE_NAMED_ERROR_RETURN_SOCKET 1
+#define DNSSD_CLIENT_TIMEOUT 10 // In seconds
+
#ifndef CTL_PATH_PREFIX
#define CTL_PATH_PREFIX "/var/tmp/dnssd_result_socket."
#endif
while (len)
{
ssize_t num_read = recv(sd, buf, len, 0);
+ // It is valid to get an interrupted system call error e.g., somebody attaching
+ // in a debugger, retry without failing
+ if ((num_read < 0) && (errno == EINTR)) { syslog(LOG_INFO, "dnssd_clientstub read_all: EINTR continue"); continue; }
if ((num_read == 0) || (num_read < 0) || (num_read > len))
{
int printWarn = 0;
return (ret > 0);
}
+// Wait for daemon to write to socket
+static int wait_for_daemon(dnssd_sock_t sock, int timeout)
+ {
+#ifndef WIN32
+ // At this point the next operation (accept() or read()) on this socket may block for a few milliseconds waiting
+ // for the daemon to respond, but that's okay -- the daemon is a trusted service and we know if won't take more
+ // than a few milliseconds to respond. So we'll forego checking for readability of the socket.
+ (void) sock;
+ (void) timeout;
+#else
+ // Windows on the other hand suffers from 3rd party software (primarily 3rd party firewall software) that
+ // interferes with proper functioning of the TCP protocol stack. Because of this and because we depend on TCP
+ // to communicate with the system service, we want to make sure that the next operation on this socket (accept() or
+ // read()) doesn't block indefinitely.
+ if (!gDaemonErr)
+ {
+ struct timeval tv;
+ fd_set set;
+
+ FD_ZERO(&set);
+ FD_SET(sock, &set);
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ if (!select((int)(sock + 1), &set, NULL, NULL, &tv))
+ {
+ syslog(LOG_WARNING, "dnssd_clientstub wait_for_daemon timed out");
+ gDaemonErr = kDNSServiceErr_Timeout;
+ }
+ }
+#endif
+ return gDaemonErr;
+ }
+
/* create_hdr
*
* allocate and initialize an ipc message header. Value of len should initially be the
x->ProcessReply = NULL;
x->AppCallback = NULL;
x->AppContext = NULL;
- x->rec = NULL;
#if _DNS_SD_LIBDISPATCH
if (x->disp_source) dispatch_release(x->disp_source);
x->disp_source = NULL;
#else
if (write_all(sdr->sockfd, (char *)hdr, datalen + sizeof(ipc_msg_hdr)) < 0)
{
- syslog(LOG_WARNING, "dnssd_clientstub deliver_request ERROR: write_all(%d, %lu bytes) failed",
+ // write_all already prints an error message if there is an error writing to
+ // the socket except for DEFUNCT. Logging here is unnecessary and also wrong
+ // in the case of DEFUNCT sockets
+ syslog(LOG_INFO, "dnssd_clientstub deliver_request ERROR: write_all(%d, %lu bytes) failed",
sdr->sockfd, (unsigned long)(datalen + sizeof(ipc_msg_hdr)));
goto cleanup;
}
// but that's okay -- the daemon is a trusted service and we know if won't take more than a few milliseconds to respond.
dnssd_sockaddr_t daddr;
dnssd_socklen_t len = sizeof(daddr);
+ if ((err = wait_for_daemon(listenfd, DNSSD_CLIENT_TIMEOUT)) != kDNSServiceErr_NoError) goto cleanup;
errsd = accept(listenfd, (struct sockaddr *)&daddr, &len);
if (!dnssd_SocketValid(errsd)) deliver_request_bailout("accept");
#else
// but that's okay -- the daemon is a trusted service and we know if won't take more than a few milliseconds to respond.
if (sdr->op == send_bpf) // Okay to use sdr->op when checking for op == send_bpf
err = kDNSServiceErr_NoError;
- else if (read_all(errsd, (char*)&err, (int)sizeof(err)) < 0)
- err = kDNSServiceErr_ServiceNotRunning; // On failure read_all will have written a message to syslog for us
- else
- err = ntohl(err);
+ else if ((err = wait_for_daemon(errsd, DNSSD_CLIENT_TIMEOUT)) == kDNSServiceErr_NoError)
+ {
+ if (read_all(errsd, (char*)&err, (int)sizeof(err)) < 0)
+ err = kDNSServiceErr_ServiceNotRunning; // On failure read_all will have written a message to syslog for us
+ else
+ err = ntohl(err);
+ }
//syslog(LOG_WARNING, "dnssd_clientstub deliver_request: retrieved error code %d", err);
{
return kDNSServiceErr_BadParam;
}
-
+
err = ConnectToServer(sdRef, flags, resolve_request, handle_resolve_response, callBack, context);
if (err) return err; // On error ConnectToServer leaves *sdRef set to NULL
# define dnssd_SocketValid(s) ((s) != INVALID_SOCKET)
# define dnssd_EWOULDBLOCK WSAEWOULDBLOCK
# define dnssd_EINTR WSAEINTR
+# define dnssd_ECONNRESET WSAECONNRESET
# define dnssd_sock_t SOCKET
# define dnssd_socklen_t int
# define dnssd_close(sock) closesocket(sock)
# define dnssd_SocketValid(s) ((s) >= 0)
# define dnssd_EWOULDBLOCK EWOULDBLOCK
# define dnssd_EINTR EINTR
+# define dnssd_ECONNRESET ECONNRESET
# define dnssd_EPIPE EPIPE
# define dnssd_sock_t int
# define dnssd_socklen_t unsigned int
typedef enum
{
request_op_none = 0, // No request yet received on this connection
- connection_request = 1, // connected socket via DNSServiceCreateConnection()
+ connection_request = 1, // connected socket via DNSServiceConnect()
reg_record_request, // reg/remove record only valid for connected sockets
remove_record_request,
enumeration_request,
#include "uDNS.h"
#include "uds_daemon.h"
+// Normally we append search domains only for queries with a single label that are not
+// fully qualified. This can be overridden to apply search domains for queries (that are
+// not fully qualified) with any number of labels e.g., moon, moon.cs, moon.cs.be, etc.
+mDNSBool AlwaysAppendSearchDomains = mDNSfalse;
+
// Apple-specific functionality, not required for other platforms
#if APPLE_OSX_mDNSResponder
#include <sys/ucred.h>
int unresponsiveness_reports;
struct reply_state *replies; // corresponding (active) reply list
req_termination_fn terminate;
+ DNSServiceFlags flags;
union
{
mDNSu32 flags;
mDNSu32 protocol;
DNSQuestion q4;
+ DNSQuestion *q42;
DNSQuestion q6;
+ DNSQuestion *q62;
} addrinfo;
struct
{
struct
{
DNSQuestion q;
- DNSQuestion q2;
+ DNSQuestion *q2;
} queryrecord;
struct
{
// Build reply header
*rep = create_reply(op, len, request);
(*rep)->rhdr->flags = dnssd_htonl(flags);
- (*rep)->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id));
+ (*rep)->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id, mDNSfalse));
(*rep)->rhdr->error = dnssd_htonl(err);
// Build reply body
// Build reply header
*rep = create_reply(op, len, request);
(*rep)->rhdr->flags = dnssd_htonl(flags);
- (*rep)->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id));
+ (*rep)->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id, mDNSfalse));
(*rep)->rhdr->error = dnssd_htonl(err);
// Build reply body
mDNSu32 ttl = GetTTL ? get_uint32(&request->msgptr, request->msgend) : 0;
int storage_size = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody);
AuthRecord *rr;
+ mDNSInterfaceID InterfaceID;
+ AuthRecType artype;
+
+ request->flags = flags;
if (str_err) { LogMsg("ERROR: read_rr_from_ipc_msg - get_string"); return NULL; }
rr = mallocL("AuthRecord/read_rr_from_ipc_msg", sizeof(AuthRecord) - sizeof(RDataBody) + storage_size);
if (!rr) FatalError("ERROR: malloc");
- mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex),
- type, 0, (mDNSu8) ((flags & kDNSServiceFlagsShared) ? kDNSRecordTypeShared : kDNSRecordTypeUnique), mDNSNULL, mDNSNULL);
+
+ InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+ if (InterfaceID == mDNSInterface_LocalOnly)
+ artype = AuthRecordLocalOnly;
+ else if (InterfaceID == mDNSInterface_P2P)
+ artype = AuthRecordP2P;
+ else if ((InterfaceID == mDNSInterface_Any) && (flags & kDNSServiceFlagsIncludeP2P))
+ artype = AuthRecordAnyIncludeP2P;
+ else
+ artype = AuthRecordAny;
+
+ mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, type, 0,
+ (mDNSu8) ((flags & kDNSServiceFlagsShared) ? kDNSRecordTypeShared : kDNSRecordTypeUnique), artype, mDNSNULL, mDNSNULL);
if (!MakeDomainNameFromDNSNameString(&rr->namestorage, name))
{
return;
}
+#if APPLE_OSX_mDNSResponder
+ // Update packet filter if p2p interface already exists, otherwise,
+ // if will be updated when we get the KEV_DL_IF_ATTACHED event for
+ // the interface. Called here since we don't call external_start_advertising_service()
+ // with the SRV record when advertising a service.
+ mDNSInitPacketFilter();
+#endif // APPLE_OSX_mDNSResponder
+
if (instance->external_advertise) LogMsg("external_start_advertising_helper: external_advertise already set!");
for ( i = 0; i < instance->request->u.servicereg.num_subtypes; i++)
int i;
if (!instance->external_advertise) return;
+
+ LogInfo("external_stop_advertising_helper: calling external_stop_advertising_service");
for ( i = 0; i < instance->request->u.servicereg.num_subtypes; i++)
- external_start_advertising_service(&st[i].resrec);
+ external_stop_advertising_service(&st[i].resrec);
external_stop_advertising_service(&instance->srs.RR_PTR.resrec);
external_stop_advertising_service(&instance->srs.RR_TXT.resrec);
LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c);
else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; }
- if (instance->request->u.servicereg.InterfaceID == mDNSInterface_P2P || (!instance->request->u.servicereg.InterfaceID && SameDomainName(&instance->domain, &localdomain)))
+ if (instance->request->u.servicereg.InterfaceID == mDNSInterface_P2P || (!instance->request->u.servicereg.InterfaceID && SameDomainName(&instance->domain, &localdomain) && (instance->request->flags & kDNSServiceFlagsIncludeP2P)))
+ {
+ LogInfo("regservice_callback: calling external_start_advertising_helper()");
external_start_advertising_helper(instance);
+ }
if (instance->request->u.servicereg.autoname && CountPeerRegistrations(m, srs) == 0)
RecordUpdatedNiceLabel(m, 0); // Successfully got new name, tell user immediately
}
else
{
if (result != mStatus_MemFree) LogMsg("regrecord_callback: error %d received after parent termination", result);
+
+ // We come here when the record is being deregistered either from DNSServiceRemoveRecord or connection_termination.
+ // If the record has been updated, we need to free the rdata. Everytime we call mDNS_Update, it calls update_callback
+ // with the old rdata (so that we can free it) and stores the new rdata in "rr->resrec.rdata". This means, we need
+ // to free the latest rdata for which the update_callback was never called with.
+ if (rr->resrec.rdata != &rr->rdatastorage) freeL("RData/regrecord_callback", rr->resrec.rdata);
freeL("AuthRecord/regrecord_callback", rr);
}
}
reply_state *reply = create_reply(reg_record_reply_op, len, request);
reply->mhdr->client_context = re->regrec_client_context;
reply->rhdr->flags = dnssd_htonl(0);
- reply->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, rr->resrec.InterfaceID));
+ reply->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, rr->resrec.InterfaceID, mDNSfalse));
reply->rhdr->error = dnssd_htonl(result);
append_reply(request, reply);
}
else
{
if (re->external_advertise) LogMsg("regrecord_callback: external_advertise already set!");
- if (re->origInterfaceID == mDNSInterface_P2P || (!re->origInterfaceID && IsLocalDomain(&rr->namestorage)))
+
+ if (re->origInterfaceID == mDNSInterface_P2P || (!re->origInterfaceID && IsLocalDomain(&rr->namestorage) && (request->flags & kDNSServiceFlagsIncludeP2P)))
{
+ LogInfo("regrecord_callback: calling external_start_advertising_service");
external_start_advertising_service(&rr->resrec);
re->external_advertise = mDNStrue;
}
AuthRecord *rr = read_rr_from_ipc_msg(request, 1, 1);
if (rr)
{
+ registered_record_entry *re;
+ // Don't allow non-local domains to be regsitered as LocalOnly. Allowing this would permit
+ // clients to register records such as www.bigbank.com A w.x.y.z to redirect Safari.
+ if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly && !IsLocalDomain(rr->resrec.name) &&
+ rr->resrec.rrclass == kDNSClass_IN && (rr->resrec.rrtype == kDNSType_A || rr->resrec.rrtype == kDNSType_AAAA ||
+ rr->resrec.rrtype == kDNSType_CNAME))
+ {
+ freeL("AuthRecord/handle_regrecord_request", rr);
+ return (mStatus_BadParamErr);
+ }
// allocate registration entry, link into list
- registered_record_entry *re = mallocL("registered_record_entry", sizeof(registered_record_entry));
+ re = mallocL("registered_record_entry", sizeof(registered_record_entry));
if (!re) FatalError("ERROR: malloc");
re->key = request->hdr.reg_index;
re->rr = rr;
rr->RecordContext = re;
rr->RecordCallback = regrecord_callback;
- re->next = request->u.reg_recs;
- request->u.reg_recs = re;
-
re->origInterfaceID = rr->resrec.InterfaceID;
if (rr->resrec.InterfaceID == mDNSInterface_P2P) rr->resrec.InterfaceID = mDNSInterface_Any;
#if 0
LogOperation("%3d: DNSServiceRegisterRecord(%u %s) START", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec));
err = mDNS_Register(&mDNSStorage, rr);
+ if (err)
+ {
+ LogOperation("%3d: DNSServiceRegisterRecord(%u %s) ERROR (%d)", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec), err);
+ freeL("registered_record_entry", re);
+ freeL("registered_record_entry/AuthRecord", rr);
+ }
+ else
+ {
+ re->next = request->u.reg_recs;
+ request->u.reg_recs = re;
+ }
}
return(err);
}
extra->r.resrec.rdlength = rdlen;
mDNSPlatformMemCopy(&extra->r.rdatastorage.u.data, rdata, rdlen);
- result = mDNS_AddRecordToService(&mDNSStorage, srs, extra, &extra->r.rdatastorage, ttl);
+ result = mDNS_AddRecordToService(&mDNSStorage, srs, extra, &extra->r.rdatastorage, ttl,
+ (request->flags & kDNSServiceFlagsIncludeP2P) ? 1: 0);
if (result) { freeL("ExtraResourceRecord/add_record_to_service", extra); return result; }
extra->ClientID = request->hdr.reg_index;
- if (instance->external_advertise && (instance->request->u.servicereg.InterfaceID == mDNSInterface_P2P || (!instance->request->u.servicereg.InterfaceID && SameDomainName(&instance->domain, &localdomain))))
+ if (instance->external_advertise && (instance->request->u.servicereg.InterfaceID == mDNSInterface_P2P || (!instance->request->u.servicereg.InterfaceID && SameDomainName(&instance->domain, &localdomain) && (instance->request->flags & kDNSServiceFlagsIncludeP2P))))
+ {
+ LogInfo("add_record_to_service: calling external_start_advertising_service");
external_start_advertising_service(&extra->r.resrec);
+ }
return result;
}
if (request->terminate != regservice_termination_callback)
{ LogMsg("%3d: DNSServiceAddRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); }
- LogOperation("%3d: DNSServiceAddRecord(%##s, %s, %d)", request->sd,
+ // For a service registered with zero port, don't allow adding records. This mostly happens due to a bug
+ // in the application. See radar://9165807.
+ if (mDNSIPPortIsZero(request->u.servicereg.port))
+ { LogMsg("%3d: DNSServiceAddRecord: adding record to a service registered with zero port", request->sd); return(mStatus_BadParamErr); }
+
+ LogOperation("%3d: DNSServiceAddRecord(%X, %##s, %s, %d)", request->sd, flags,
(request->u.servicereg.instances) ? request->u.servicereg.instances->srs.RR_SRV.resrec.name->c : NULL, DNSTypeName(rrtype), rdlen);
for (i = request->u.servicereg.instances; i; i = i->next)
if (ext.rdlength == oldrdlen && mDNSPlatformMemSame(&ext.rdata->u, &oldrd->u, oldrdlen)) goto exit;
SetNewRData(&ext, oldrd, oldrdlen);
external_stop_advertising_service(&ext);
+ LogInfo("update_callback: calling external_start_advertising_service");
external_start_advertising_service(&rr->resrec);
}
exit:
if (request->terminate != regservice_termination_callback)
{ LogMsg("%3d: DNSServiceUpdateRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); }
+ // For a service registered with zero port, only SRV record is initialized. Don't allow any updates.
+ if (mDNSIPPortIsZero(request->u.servicereg.port))
+ { LogMsg("%3d: DNSServiceUpdateRecord: updating the record of a service registered with zero port", request->sd); return(mStatus_BadParamErr); }
+
// update the saved off TXT data for the service
if (hdr->reg_index == TXT_RECORD_INDEX)
{
if (!st) return(mDNSNULL);
for (i = 0; i < NumSubTypes; i++)
{
- mDNS_SetupResourceRecord(&st[i], mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, mDNSNULL, mDNSNULL);
+ mDNS_SetupResourceRecord(&st[i], mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, AuthRecordAny, mDNSNULL, mDNSNULL);
while (*p) p++;
p++;
if (!MakeDomainNameFromDNSNameString(&st[i].namestorage, p))
const mDNSBool DomainIsLocal = SameDomainName(domain, &localdomain);
mStatus result;
mDNSInterfaceID interfaceID = request->u.servicereg.InterfaceID;
+ mDNSu32 regFlags = 0;
+
+ if (interfaceID == mDNSInterface_P2P)
+ {
+ interfaceID = mDNSInterface_Any;
+ regFlags |= regFlagIncludeP2P;
+ }
+ else if (request->flags & kDNSServiceFlagsIncludeP2P)
+ regFlags |= regFlagIncludeP2P;
- if (interfaceID == mDNSInterface_P2P) interfaceID = mDNSInterface_Any;
+ // client guarantees that record names are unique
+ if (request->flags & kDNSServiceFlagsForce)
+ regFlags |= regFlagKnownUnique;
// If the client specified an interface, but no domain, then we honor the specified interface for the "local" (mDNS)
// registration but for the wide-area registrations we don't (currently) have any concept of a wide-area unicast
request->u.servicereg.port,
request->u.servicereg.txtdata, request->u.servicereg.txtlen,
instance->subtypes, request->u.servicereg.num_subtypes,
- interfaceID, regservice_callback, instance);
+ interfaceID, regservice_callback, instance, regFlags);
if (!result)
{
get_string(&request->msgptr, request->msgend, host, MAX_ESCAPED_DOMAIN_NAME) < 0)
{ LogMsg("ERROR: handle_regservice_request - Couldn't read name/regtype/domain"); return(mStatus_BadParamErr); }
+ request->flags = flags;
request->u.servicereg.InterfaceID = InterfaceID;
request->u.servicereg.instances = NULL;
request->u.servicereg.txtlen = 0;
count+1, srv.c, mDNSVal16(request->u.servicereg.port));
}
- LogOperation("%3d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", \"%s\", %u) START",
- request->sd, name, request->u.servicereg.type_as_string, domain, host, mDNSVal16(request->u.servicereg.port));
+ LogOperation("%3d: DNSServiceRegister(%X, %d, \"%s\", \"%s\", \"%s\", \"%s\", %u) START",
+ request->sd, flags, interfaceIndex, name, request->u.servicereg.type_as_string, domain, host, mDNSVal16(request->u.servicereg.port));
// We need to unconditionally set request->terminate, because even if we didn't successfully
// start any registrations right now, subsequent configuration changes may cause successful
LogOperation("%3d: DNSServiceBrowse(%##s, %s) RESULT %s %d: %s",
req->sd, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "Add" : "Rmv",
- mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID), RRDisplayString(m, answer));
+ mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNSfalse), RRDisplayString(m, answer));
append_reply(req, rep);
}
b->next = info->u.browser.browsers;
info->u.browser.browsers = b;
LogOperation("%3d: DNSServiceBrowse(%##s) START", info->sd, b->q.qname.c);
- if (info->u.browser.interface_id == mDNSInterface_P2P || (!info->u.browser.interface_id && SameDomainName(&b->domain, &localdomain)))
+ if (info->u.browser.interface_id == mDNSInterface_P2P || (!info->u.browser.interface_id && SameDomainName(&b->domain, &localdomain) && (info->flags & kDNSServiceFlagsIncludeP2P)))
{
domainname tmp;
ConstructServiceName(&tmp, NULL, &info->u.browser.regtype, &b->domain);
+ LogInfo("add_domain_to_browser: calling external_start_browsing_for_service()");
external_start_browsing_for_service(&mDNSStorage, &tmp, kDNSType_PTR);
}
}
{
browser_t *ptr = info->u.browser.browsers;
- if (info->u.browser.interface_id == mDNSInterface_P2P || (!info->u.browser.interface_id && SameDomainName(&ptr->domain, &localdomain)))
+ if (info->u.browser.interface_id == mDNSInterface_P2P || (!info->u.browser.interface_id && SameDomainName(&ptr->domain, &localdomain) && (info->flags & kDNSServiceFlagsIncludeP2P)))
{
domainname tmp;
ConstructServiceName(&tmp, NULL, &info->u.browser.regtype, &ptr->domain);
+ LogInfo("browse_termination_callback: calling external_stop_browsing_for_service()");
external_stop_browsing_for_service(&mDNSStorage, &tmp, kDNSType_PTR);
}
(type == mDNS_DomainTypeRegistration ) ? "registration dom" :
(type == mDNS_DomainTypeBrowseAutomatic) ? "automatic browse" : "?", d->c);
- mDNS_SetupResourceRecord(&ptr->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, FreeARElemCallback, ptr);
+ mDNS_SetupResourceRecord(&ptr->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, AuthRecordLocalOnly, FreeARElemCallback, ptr);
MakeDomainNameFromDNSNameString(&ptr->ar.namestorage, mDNS_DomainTypeNames[type]);
AppendDNSNameString (&ptr->ar.namestorage, "local");
AssignDomainName(&ptr->ar.resrec.rdata->u.name, d);
if (num_autoname > 0)
{
mDNSu8 len = m->HIHardware.c[0] < 255 - 6 ? m->HIHardware.c[0] : 255 - 6;
- mDNS_SetupResourceRecord(&m->DeviceInfo, mDNSNULL, mDNSNULL, kDNSType_TXT, kStandardTTL, kDNSRecordTypeAdvisory, mDNSNULL, mDNSNULL);
+ mDNS_SetupResourceRecord(&m->DeviceInfo, mDNSNULL, mDNSNULL, kDNSType_TXT, kStandardTTL, kDNSRecordTypeAdvisory, AuthRecordAny, mDNSNULL, mDNSNULL);
ConstructServiceName(&m->DeviceInfo.namestorage, &m->nicelabel, &DeviceInfoName, &localdomain);
mDNSPlatformMemCopy(m->DeviceInfo.resrec.rdata->u.data + 1, "model=", 6);
mDNSPlatformMemCopy(m->DeviceInfo.resrec.rdata->u.data + 7, m->HIHardware.c + 1, len);
if (domain[0] == '\0') uDNS_SetupSearchDomains(&mDNSStorage, UDNS_START_WAB_QUERY);
+ request->flags = flags;
typedn.c[0] = 0;
NumSubTypes = ChopSubTypes(regtype); // Note: Modifies regtype string to remove trailing subtypes
if (NumSubTypes < 0 || NumSubTypes > 1) return(mStatus_BadParamErr);
request->u.browser.default_domain = !domain[0];
request->u.browser.browsers = NULL;
- LogOperation("%3d: DNSServiceBrowse(\"%##s\", \"%s\") START", request->sd, request->u.browser.regtype.c, domain);
+ LogOperation("%3d: DNSServiceBrowse(%X, %d, \"%##s\", \"%s\") START",
+ request->sd, request->flags, interfaceIndex, request->u.browser.regtype.c, domain);
// We need to unconditionally set request->terminate, because even if we didn't successfully
// start any browses right now, subsequent configuration changes may cause successful
// allocate/init reply header
rep = create_reply(resolve_reply_op, len, req);
rep->rhdr->flags = dnssd_htonl(0);
- rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID));
+ rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNSfalse));
rep->rhdr->error = dnssd_htonl(kDNSServiceErr_NoError);
data = (char *)&rep->rhdr[1];
mDNSBool wasP2P = (interfaceIndex == kDNSServiceInterfaceIndexP2P);
+ request->flags = flags;
if (wasP2P) interfaceIndex = kDNSServiceInterfaceIndexAny;
InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
request->u.resolve.qsrv.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0;
request->u.resolve.qsrv.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
request->u.resolve.qsrv.SuppressUnusable = mDNSfalse;
- request->u.resolve.qsrv.WakeOnResolve = (flags & kDNSServiceFlagsWakeOnResolve ) != 0;
+ request->u.resolve.qsrv.SearchListIndex = 0;
+ request->u.resolve.qsrv.AppendSearchDomains = 0;
+ request->u.resolve.qsrv.RetryWithSearchDomains = mDNSfalse;
+ request->u.resolve.qsrv.TimeoutQuestion = 0;
+ request->u.resolve.qsrv.WakeOnResolve = (flags & kDNSServiceFlagsWakeOnResolve) != 0;
+ request->u.resolve.qsrv.qnameOrig = mDNSNULL;
request->u.resolve.qsrv.QuestionCallback = resolve_result_callback;
request->u.resolve.qsrv.QuestionContext = request;
request->u.resolve.qtxt.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0;
request->u.resolve.qtxt.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
request->u.resolve.qtxt.SuppressUnusable = mDNSfalse;
- request->u.resolve.qtxt.WakeOnResolve = mDNSfalse;
+ request->u.resolve.qtxt.SearchListIndex = 0;
+ request->u.resolve.qtxt.AppendSearchDomains = 0;
+ request->u.resolve.qtxt.RetryWithSearchDomains = mDNSfalse;
+ request->u.resolve.qtxt.TimeoutQuestion = 0;
+ request->u.resolve.qtxt.WakeOnResolve = 0;
+ request->u.resolve.qtxt.qnameOrig = mDNSNULL;
request->u.resolve.qtxt.QuestionCallback = resolve_result_callback;
request->u.resolve.qtxt.QuestionContext = request;
{
request->terminate = resolve_termination_callback;
// If the user explicitly passed in P2P, we don't restrict the domain in which we resolve.
- if (wasP2P || (!InterfaceID && IsLocalDomain(&fqdn)))
- { request->u.resolve.external_advertise = mDNStrue; external_start_resolving_service(&fqdn);}
+ if (wasP2P || (!InterfaceID && IsLocalDomain(&fqdn) && (request->flags & kDNSServiceFlagsIncludeP2P)))
+ {
+ request->u.resolve.external_advertise = mDNStrue;
+ LogInfo("handle_resolve_request: calling external_start_resolving_service()");
+ external_start_resolving_service(&fqdn);
+ }
}
}
// to the mDNSCore routine) that sends results back to the client, and a termination routine that aborts
// the mDNSCore operation if the client dies or closes its socket.
+// Returns -1 to tell the caller that it should not try to reissue the query anymore
+// Returns 1 on successfully appending a search domain and the caller should reissue the new query
+// Returns 0 when there are no more search domains and the caller should reissue the query
+mDNSlocal int AppendNewSearchDomain(mDNS *const m, DNSQuestion *question)
+ {
+ domainname *sd;
+ mStatus err;
+
+ // Sanity check: The caller already checks this. We use -1 to indicate that we have searched all
+ // the domains and should try the single label query directly on the wire.
+ if (question->SearchListIndex == -1)
+ {
+ LogMsg("AppendNewSearchDomain: question %##s (%s) SearchListIndex is -1", question->qname.c, DNSTypeName(question->qtype));
+ return -1;
+ }
+
+ if (!question->AppendSearchDomains)
+ {
+ LogMsg("AppendNewSearchDomain: question %##s (%s) AppendSearchDoamins is 0", question->qname.c, DNSTypeName(question->qtype));
+ return -1;
+ }
+
+ // Save the original name, before we modify them below.
+ if (!question->qnameOrig)
+ {
+ question->qnameOrig = mallocL("AppendNewSearchDomain", sizeof(domainname));
+ if (!question->qnameOrig) { LogMsg("AppendNewSearchDomain: ERROR!! malloc failure"); return -1; }
+ question->qnameOrig->c[0] = 0;
+ AssignDomainName(question->qnameOrig, &question->qname);
+ LogInfo("AppendSearchDomain: qnameOrig %##s", question->qnameOrig->c);
+ }
+
+ sd = uDNS_GetNextSearchDomain(m, question->InterfaceID, &question->SearchListIndex, !question->AppendLocalSearchDomains);
+ // We use -1 to indicate that we have searched all the domains and should try the single label
+ // query directly on the wire. uDNS_GetNextSearchDomain should never return a negative value
+ if (question->SearchListIndex == -1)
+ {
+ LogMsg("AppendNewSearchDomain: ERROR!! uDNS_GetNextSearchDomain returned -1");
+ return -1;
+ }
+
+ // Not a common case. Perhaps, we should try the next search domain if it exceeds ?
+ if (sd && (DomainNameLength(question->qnameOrig) + DomainNameLength(sd)) > MAX_DOMAIN_NAME)
+ {
+ LogMsg("AppendNewSearchDomain: ERROR!! exceeding max domain length for %##s (%s) SearchDomain %##s length %d, Question name length %d", question->qnameOrig->c, DNSTypeName(question->qtype), sd->c, DomainNameLength(question->qnameOrig), DomainNameLength(sd));
+ return -1;
+ }
+
+ // if there are no more search domains and we have already tried this question
+ // without appending search domains, then we are done.
+ if (!sd && !ApplySearchDomainsFirst(question))
+ {
+ LogInfo("AppnedNewSearchDomain: No more search domains for question with name %##s (%s), not trying anymore", question->qname.c, DNSTypeName(question->qtype));
+ return -1;
+ }
+
+ // Stop the question before changing the name as negative cache entries could be pointing at this question.
+ // Even if we don't change the question in the case of returning 0, the caller is going to restart the
+ // question.
+ err = mDNS_StopQuery(&mDNSStorage, question);
+ if (err) { LogMsg("AppendNewSearchDomain: ERROR!! %##s %s mDNS_StopQuery: %d, while retrying with search domains", question->qname.c, DNSTypeName(question->qtype), (int)err); }
+
+ AssignDomainName(&question->qname, question->qnameOrig);
+ if (sd)
+ {
+ AppendDomainName(&question->qname, sd);
+ LogInfo("AppnedNewSearchDomain: Returning question with name %##s, SearchListIndex %d", question->qname.c, question->SearchListIndex);
+ return 1;
+ }
+
+ // Try the question as single label
+ LogInfo("AppnedNewSearchDomain: No more search domains for question with name %##s (%s), trying one last time", question->qname.c, DNSTypeName(question->qtype));
+ return 0;
+ }
+
+#if APPLE_OSX_mDNSResponder
+
+mDNSlocal mDNSBool DomainInSearchList(domainname *domain)
+ {
+ const SearchListElem *s;
+ for (s=SearchList; s; s=s->next)
+ if (SameDomainName(&s->domain, domain)) return mDNStrue;
+ return mDNSfalse;
+ }
+
+// Workaround for networks using Microsoft Active Directory using "local" as a private internal
+// top-level domain
+mDNSlocal mStatus SendAdditionalQuery(DNSQuestion *q, request_state *request, mStatus err)
+ {
+ extern domainname ActiveDirectoryPrimaryDomain;
+ DNSQuestion **question2;
+ #define VALID_MSAD_SRV_TRANSPORT(T) (SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_tcp") || SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_udp"))
+ #define VALID_MSAD_SRV(Q) ((Q)->qtype == kDNSType_SRV && VALID_MSAD_SRV_TRANSPORT(SecondLabel(&(Q)->qname)))
+
+ question2 = mDNSNULL;
+ if (request->hdr.op == query_request)
+ question2 = &request->u.queryrecord.q2;
+ else if (request->hdr.op == addrinfo_request)
+ {
+ if (q->qtype == kDNSType_A)
+ question2 = &request->u.addrinfo.q42;
+ else if (q->qtype == kDNSType_AAAA)
+ question2 = &request->u.addrinfo.q62;
+ }
+ if (!question2)
+ {
+ LogMsg("SendAdditionalQuery: question2 NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+ return mStatus_BadParamErr;
+ }
+
+ // Sanity check: If we already sent an additonal query, we don't need to send one more.
+ //
+ // 1. When the application calls DNSServiceQueryRecord or DNSServiceGetAddrInfo with a .local name, this function
+ // is called to see whether a unicast query should be sent or not.
+ //
+ // 2. As a result of appending search domains, the question may be end up with a .local suffix even though it
+ // was not a .local name to start with. In that case, queryrecord_result_callback calls this function to
+ // send the additional query.
+ //
+ // Thus, it should not be called more than once.
+ if (*question2)
+ {
+ LogInfo("SendAdditionalQuery: question2 already sent for %##s (%s), no more q2", q->qname.c, DNSTypeName(q->qtype));
+ return err;
+ }
+
+ if (!q->ForceMCast && SameDomainLabel(LastLabel(&q->qname), (const mDNSu8 *)&localdomain))
+ if (q->qtype == kDNSType_A || q->qtype == kDNSType_AAAA || VALID_MSAD_SRV(q))
+ {
+ DNSQuestion *q2;
+ int labels = CountLabels(&q->qname);
+ q2 = mallocL("DNSQuestion", sizeof(DNSQuestion));
+ if (!q2) FatalError("ERROR: SendAdditionalQuery malloc");
+ *question2 = q2;
+ *q2 = *q;
+ q2->InterfaceID = mDNSInterface_Unicast;
+ q2->ExpectUnique = mDNStrue;
+ // If the query starts as a single label e.g., somehost, and we have search domains with .local,
+ // queryrecord_result_callback calls this function when .local is appended to "somehost".
+ // At that time, the name in "q" is pointing at somehost.local and its qnameOrig pointing at
+ // "somehost". We need to copy that information so that when we retry with a different search
+ // domain e.g., mycompany.local, we get "somehost.mycompany.local".
+ if (q->qnameOrig)
+ {
+ (*question2)->qnameOrig = mallocL("SendAdditionalQuery", DomainNameLength(q->qnameOrig));
+ if (!(*question2)->qnameOrig) { LogMsg("SendAdditionalQuery: ERROR!! malloc failure"); return mStatus_NoMemoryErr; }
+ (*question2)->qnameOrig->c[0] = 0;
+ AssignDomainName((*question2)->qnameOrig, q->qnameOrig);
+ LogInfo("SendAdditionalQuery: qnameOrig %##s", (*question2)->qnameOrig->c);
+ }
+ // For names of the form "<one-or-more-labels>.bar.local." we always do a second unicast query in parallel.
+ // For names of the form "<one-label>.local." it's less clear whether we should do a unicast query.
+ // If the name being queried is exactly the same as the name in the DHCP "domain" option (e.g. the DHCP
+ // "domain" is my-small-company.local, and the user types "my-small-company.local" into their web browser)
+ // then that's a hint that it's worth doing a unicast query. Otherwise, we first check to see if the
+ // site's DNS server claims there's an SOA record for "local", and if so, that's also a hint that queries
+ // for names in the "local" domain will be safely answered privately before they hit the root name servers.
+ // Note that in the "my-small-company.local" example above there will typically be an SOA record for
+ // "my-small-company.local" but *not* for "local", which is why the "local SOA" check would fail in that case.
+ // We need to check against both ActiveDirectoryPrimaryDomain and SearchList. If it matches against either
+ // of those, we don't want do the SOA check for the local
+ if (labels == 2 && !SameDomainName(&q->qname, &ActiveDirectoryPrimaryDomain) && !DomainInSearchList(&q->qname))
+ {
+ AssignDomainName(&q2->qname, &localdomain);
+ q2->qtype = kDNSType_SOA;
+ q2->LongLived = mDNSfalse;
+ q2->ForceMCast = mDNSfalse;
+ q2->ReturnIntermed = mDNStrue;
+ // Don't append search domains for the .local SOA query
+ q2->AppendSearchDomains = 0;
+ q2->AppendLocalSearchDomains = 0;
+ q2->RetryWithSearchDomains = mDNSfalse;
+ q2->SearchListIndex = 0;
+ q2->TimeoutQuestion = 0;
+ }
+ LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast", request->sd, q2->qname.c, DNSTypeName(q2->qtype));
+ err = mDNS_StartQuery(&mDNSStorage, q2);
+ if (err) LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q2->qname.c, DNSTypeName(q2->qtype), (int)err);
+ }
+ return(err);
+ }
+#endif // APPLE_OSX_mDNSResponder
+
+// This function tries to append a search domain if valid and possible. If so, returns true.
+mDNSlocal mDNSBool RetryQuestionWithSearchDomains(mDNS *const m, DNSQuestion *question, request_state *req)
+ {
+ int result;
+ // RetryWithSearchDomains tells the core to call us back so that we can retry with search domains if there is no
+ // answer in the cache or /etc/hosts. In the first call back from the core, we clear RetryWithSearchDomains so
+ // that we don't get called back repeatedly. If we got an answer from the cache or /etc/hosts, we don't touch
+ // RetryWithSearchDomains which may or may not be set.
+ //
+ // If we get e.g., NXDOMAIN and the query is neither suppressed nor exhausted the domain search list and
+ // is a valid question for appending search domains, retry by appending domains
+
+ if (!question->SuppressQuery && question->SearchListIndex != -1 && question->AppendSearchDomains)
+ {
+ question->RetryWithSearchDomains = 0;
+ result = AppendNewSearchDomain(m, question);
+ // As long as the result is either zero or 1, we retry the question. If we exahaust the search
+ // domains (result is zero) we try the original query (as it was before appending the search
+ // domains) as such on the wire as a last resort if we have not tried them before. For queries
+ // with more than one label, we have already tried them before appending search domains and
+ // hence don't retry again
+ if (result != -1)
+ {
+ mStatus err;
+ err = mDNS_StartQuery(m, question);
+ if (!err)
+ {
+ LogOperation("%3d: RetryQuestionWithSearchDomains(%##s, %s), retrying after appending search domain", req->sd, question->qname.c, DNSTypeName(question->qtype));
+ // If the result was zero, it meant that there are no search domains and we just retried the question
+ // as a single label and we should not retry with search domains anymore.
+ if (!result) question->SearchListIndex = -1;
+ return mDNStrue;
+ }
+ else
+ {
+ LogMsg("%3d: ERROR: RetryQuestionWithSearchDomains %##s %s mDNS_StartQuery: %d, while retrying with search domains", req->sd, question->qname.c, DNSTypeName(question->qtype), (int)err);
+ // We have already stopped the query and could not restart. Reset the appropriate pointers
+ // so that we don't call stop again when the question terminates
+ question->QuestionContext = mDNSNULL;
+ }
+ }
+ }
+ else
+ {
+ LogInfo("%3d: RetryQuestionWithSearchDomains: Not appending search domains - SuppressQuery %d, SearchListIndex %d, AppendSearchDomains %d", req->sd, question->SuppressQuery, question->SearchListIndex, question->AppendSearchDomains);
+ }
+ return mDNSfalse;
+ }
+
mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
{
char name[MAX_ESCAPED_DOMAIN_NAME];
char *data;
size_t len;
DNSServiceErrorType error = kDNSServiceErr_NoError;
- (void)m; // Unused
+ DNSQuestion *q = mDNSNULL;
#if APPLE_OSX_mDNSResponder
- if (question == &req->u.queryrecord.q2 && question->qtype != req->u.queryrecord.q.qtype && !SameDomainName(&question->qname, &req->u.queryrecord.q.qname))
+ {
+ // Sanity check: QuestionContext is set to NULL after we stop the question and hence we should not
+ // get any callbacks from the core after this.
+ if (!req)
+ {
+ LogMsg("queryrecord_result_callback: ERROR!! QuestionContext NULL for %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
+ return;
+ }
+ if (req->hdr.op == query_request && question == req->u.queryrecord.q2)
+ q = &req->u.queryrecord.q;
+ else if (req->hdr.op == addrinfo_request && question == req->u.addrinfo.q42)
+ q = &req->u.addrinfo.q4;
+ else if (req->hdr.op == addrinfo_request && question == req->u.addrinfo.q62)
+ q = &req->u.addrinfo.q6;
+
+ if (q && question->qtype != q->qtype && !SameDomainName(&question->qname, &q->qname))
{
- mDNS_StopQuery(&mDNSStorage, question);
- question->QuestionCallback = mDNSNULL;
+ mStatus err;
+ domainname *orig = question->qnameOrig;
+
+ LogInfo("queryrecord_result_callback: Stopping q2 local %##s", question->qname.c);
+ mDNS_StopQuery(m, question);
+ question->QuestionContext = mDNSNULL;
+
+ // We got a negative response for the SOA record indicating that .local does not exist.
+ // But we might have other search domains (that does not end in .local) that can be
+ // appended to this question. In that case, we want to retry the question. Otherwise,
+ // we don't want to try this question as unicast.
+ if (answer->RecordType == kDNSRecordTypePacketNegative && !q->AppendSearchDomains)
+ {
+ LogInfo("queryrecord_result_callback: question %##s AppendSearchDomains zero", q->qname.c);
+ return;
+ }
+
// If we got a non-negative answer for our "local SOA" test query, start an additional parallel unicast query
- if (answer->RecordType != kDNSRecordTypePacketNegative)
+ //
+ // Note: When we copy the original question, we copy everything including the AppendSearchDomains,
+ // RetryWithSearchDomains except for qnameOrig which can be non-NULL if the original question is
+ // e.g., somehost and then we appended e.g., ".local" and retried that question. See comment in
+ // SendAdditionalQuery as to how qnameOrig gets initialized.
+ *question = *q;
+ question->InterfaceID = mDNSInterface_Unicast;
+ question->ExpectUnique = mDNStrue;
+ question->qnameOrig = orig;
+
+ LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast, context %p", req->sd, question->qname.c, DNSTypeName(question->qtype), question->QuestionContext);
+
+ // If the original question timed out, its QuestionContext would already be set to NULL and that's what we copied above.
+ // Hence, we need to set it explicitly here.
+ question->QuestionContext = req;
+ err = mDNS_StartQuery(m, question);
+ if (err) LogMsg("%3d: ERROR: queryrecord_result_callback %##s %s mDNS_StartQuery: %d", req->sd, question->qname.c, DNSTypeName(question->qtype), (int)err);
+
+ // If we got a positive response to local SOA, then try the .local question as unicast
+ if (answer->RecordType != kDNSRecordTypePacketNegative) return;
+
+ // Fall through and get the next search domain. The question is pointing at .local
+ // and we don't want to try that. Try the next search domain. Don't try with local
+ // search domains for the unicast question anymore.
+ //
+ // Note: we started the question above which will be stopped immediately (never sent on the wire)
+ // before we pick the next search domain below. RetryQuestionWithSearchDomains assumes that the
+ // question has already started.
+ question->AppendLocalSearchDomains = 0;
+ }
+
+ if (q && AddRecord && (question->InterfaceID == mDNSInterface_Unicast) && !answer->rdlength)
+ {
+ // If we get a negative response to the unicast query that we sent above, retry after appending search domains
+ // Note: We could have appended search domains below (where do it for regular unicast questions) instead of doing it here.
+ // As we ignore negative unicast answers below, we would never reach the code where the search domains are appended.
+ // To keep things simple, we handle unicast ".local" separately here.
+ LogInfo("queryrecord_result_callback: Retrying .local question %##s (%s) as unicast after appending search domains", question->qname.c, DNSTypeName(question->qtype));
+ if (RetryQuestionWithSearchDomains(m, question, req))
+ return;
+ if (question->AppendSearchDomains && !question->AppendLocalSearchDomains && IsLocalDomain(&question->qname))
{
- *question = req->u.queryrecord.q;
- question->InterfaceID = mDNSInterface_Unicast;
- question->ExpectUnique = mDNStrue;
- LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast", req->sd, question->qname.c, DNSTypeName(question->qtype));
- mStatus err = mDNS_StartQuery(&mDNSStorage, question);
- if (err) LogMsg("%3d: ERROR: queryrecord_result_callback %##s %s mDNS_StartQuery: %d", req->sd, question->qname.c, DNSTypeName(question->qtype), (int)err);
+ // If "local" is the last search domain, we need to stop the question so that we don't send the "local"
+ // question on the wire as we got a negative response for the local SOA. But, we can't stop the question
+ // yet as we may have to timeout the question (done by the "core") for which we need to leave the question
+ // in the list. We leave it disabled so that it does not hit the wire.
+ LogInfo("queryrecord_result_callback: Disabling .local question %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
+ question->ThisQInterval = 0;
}
- return;
}
+ // If we are here it means that either "question" is not "q2" OR we got a positive response for "q2" OR we have no more search
+ // domains to append for "q2". In all cases, fall through and deliver the response
+ }
#endif // APPLE_OSX_mDNSResponder
if (answer->RecordType == kDNSRecordTypePacketNegative)
{
+ // If this question needs to be timed out and we have reached the stop time, mark
+ // the error as timeout. It is possible that we might get a negative response from an
+ // external DNS server at the same time when this question reaches its stop time. We
+ // can't tell the difference as there is no indication in the callback. This should
+ // be okay as we will be timing out this query anyway.
+ mDNS_Lock(m);
+ if (question->TimeoutQuestion)
+ {
+ if ((m->timenow - question->StopTime) >= 0)
+ {
+ LogInfo("queryrecord_result_callback:Question %##s (%s) timing out, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID);
+ error = kDNSServiceErr_Timeout;
+ }
+ }
+ mDNS_Unlock(m);
// When we're doing parallel unicast and multicast queries for dot-local names (for supporting Microsoft
// Active Directory sites) we need to ignore negative unicast answers. Otherwise we'll generate negative
// answers for just about every single multicast name we ever look up, since the Microsoft Active Directory
// server is going to assert that pretty much every single multicast name doesn't exist.
- if (!answer->InterfaceID && IsLocalDomain(answer->name)) return;
- error = kDNSServiceErr_NoSuchRecord;
+ //
+ // If we are timing out this query, we need to deliver the negative answer to the application
+ if (error != kDNSServiceErr_Timeout)
+ {
+ if (!answer->InterfaceID && IsLocalDomain(answer->name))
+ {
+ LogInfo("queryrecord_result_callback:Question %##s (%s) answering local with unicast", question->qname.c, DNSTypeName(question->qtype));
+ return;
+ }
+ error = kDNSServiceErr_NoSuchRecord;
+ }
AddRecord = mDNStrue;
}
+ // If we get a negative answer, try appending search domains. Don't append search domains
+ // - if we are timing out this question
+ // - if the negative response was received as a result of a multicast query
+ // - if this is an additional query (q2), we already appended search domains above (indicated by "!q" below)
+ if (error != kDNSServiceErr_Timeout)
+ {
+ if (!q && !answer->InterfaceID && !answer->rdlength && AddRecord)
+ {
+ // If the original question did not end in .local, we did not send an SOA query
+ // to figure out whether we should send an additional unicast query or not. If we just
+ // appended .local, we need to see if we need to send an additional query. This should
+ // normally happen just once because after we append .local, we ignore all negative
+ // responses for .local above.
+ LogInfo("queryrecord_result_callback: Retrying question %##s (%s) after appending search domains", question->qname.c, DNSTypeName(question->qtype));
+ if (RetryQuestionWithSearchDomains(m, question, req))
+ {
+ // Note: We need to call SendAdditionalQuery every time after appending a search domain as .local could
+ // be anywhere in the search domain list.
+#if APPLE_OSX_mDNSResponder
+ mStatus err = mStatus_NoError;
+ err = SendAdditionalQuery(question, req, err);
+ if (err) LogMsg("queryrecord_result_callback: Sending .local SOA query failed, after appending domains");
+#endif // APPLE_OSX_mDNSResponder
+ return;
+ }
+ }
+ }
ConvertDomainNameToCString(answer->name, name);
rep = create_reply(req->hdr.op == query_request ? query_reply_op : addrinfo_reply_op, len, req);
rep->rhdr->flags = dnssd_htonl(AddRecord ? kDNSServiceFlagsAdd : 0);
- rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID));
+ // Call mDNSPlatformInterfaceIndexfromInterfaceID, but suppressNetworkChange (last argument). Otherwise, if the
+ // InterfaceID is not valid, then it simulates a "NetworkChanged" which in turn makes questions
+ // to be stopped and started including *this* one. Normally the InterfaceID is valid. But when we
+ // are using the /etc/hosts entries to answer a question, the InterfaceID may not be known to the
+ // mDNS core . Eventually, we should remove the calls to "NetworkChanged" in
+ // mDNSPlatformInterfaceIndexfromInterfaceID when it can't find InterfaceID as ResourceRecords
+ // should not have existed to answer this question if the corresponding interface is not valid.
+ rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNStrue));
rep->rhdr->error = dnssd_htonl(error);
data = (char *)&rep->rhdr[1];
put_uint32(AddRecord ? answer->rroriginalttl : 0, &data);
append_reply(req, rep);
+ // Stop the question, if we just timed out
+ if (error == kDNSServiceErr_Timeout)
+ {
+ mDNS_StopQuery(m, question);
+ // Reset the pointers so that we don't call stop on termination
+ question->QuestionContext = mDNSNULL;
+ }
#if APPLE_OSX_mDNSResponder
#if ! NO_WCF
CHECK_WCF_FUNCTION(WCFIsServerRunning)
{
LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) STOP",
request->sd, request->u.queryrecord.q.qname.c, DNSTypeName(request->u.queryrecord.q.qtype));
- mDNS_StopQuery(&mDNSStorage, &request->u.queryrecord.q); // no need to error check
- if (request->u.queryrecord.q.InterfaceID == mDNSInterface_P2P || (!request->u.queryrecord.q.InterfaceID && SameDomainName((const domainname *)LastLabel(&request->u.queryrecord.q.qname), &localdomain)))
+ if (request->u.queryrecord.q.QuestionContext)
+ {
+ mDNS_StopQuery(&mDNSStorage, &request->u.queryrecord.q); // no need to error check
+ request->u.queryrecord.q.QuestionContext = mDNSNULL;
+ }
+ else
+ {
+ DNSQuestion *question = &request->u.queryrecord.q;
+ LogInfo("queryrecord_termination_callback: question %##s (%s) already stopped, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID);
+ }
+
+ if (request->u.queryrecord.q.qnameOrig)
+ {
+ freeL("QueryTermination", request->u.queryrecord.q.qnameOrig);
+ request->u.queryrecord.q.qnameOrig = mDNSNULL;
+ }
+ if (request->u.queryrecord.q.InterfaceID == mDNSInterface_P2P || (!request->u.queryrecord.q.InterfaceID && SameDomainName((const domainname *)LastLabel(&request->u.queryrecord.q.qname), &localdomain) && (request->flags & kDNSServiceFlagsIncludeP2P)))
+ {
+ LogInfo("queryrecord_termination_callback: calling external_stop_browsing_for_service()");
external_stop_browsing_for_service(&mDNSStorage, &request->u.queryrecord.q.qname, request->u.queryrecord.q.qtype);
- if (request->u.queryrecord.q2.QuestionCallback) mDNS_StopQuery(&mDNSStorage, &request->u.queryrecord.q2);
+ }
+ if (request->u.queryrecord.q2)
+ {
+ if (request->u.queryrecord.q2->QuestionContext)
+ {
+ LogInfo("queryrecord_termination_callback: Stopping q2 %##s", request->u.queryrecord.q2->qname.c);
+ mDNS_StopQuery(&mDNSStorage, request->u.queryrecord.q2);
+ }
+ else
+ {
+ DNSQuestion *question = request->u.queryrecord.q2;
+ LogInfo("queryrecord_termination_callback: q2 %##s (%s) already stopped, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID);
+ }
+ if (request->u.queryrecord.q2->qnameOrig)
+ {
+ LogInfo("queryrecord_termination_callback: freeing q2 qnameOrig %##s", request->u.queryrecord.q2->qnameOrig->c);
+ freeL("QueryTermination q2", request->u.queryrecord.q2->qnameOrig);
+ request->u.queryrecord.q2->qnameOrig = mDNSNULL;
+ }
+ freeL("queryrecord Q2", request->u.queryrecord.q2);
+ request->u.queryrecord.q2 = mDNSNULL;
+ }
}
mDNSlocal mStatus handle_queryrecord_request(request_state *request)
if (!request->msgptr)
{ LogMsg("%3d: DNSServiceQueryRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+ request->flags = flags;
mDNSPlatformMemZero(&request->u.queryrecord, sizeof(request->u.queryrecord));
q->InterfaceID = InterfaceID;
q->ExpectUnique = mDNSfalse;
q->ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0;
q->ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
- q->SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable) != 0;
- q->WakeOnResolve = mDNSfalse;
+ q->SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable ) != 0;
+ q->TimeoutQuestion = (flags & kDNSServiceFlagsTimeout ) != 0;
+ q->WakeOnResolve = 0;
q->QuestionCallback = queryrecord_result_callback;
q->QuestionContext = request;
+ q->SearchListIndex = 0;
+
+ // Don't append search domains for fully qualified domain names including queries
+ // such as e.g., "abc." that has only one label. We convert all names to FQDNs as internally
+ // we only deal with FQDNs. Hence, we cannot look at qname to figure out whether we should
+ // append search domains or not. So, we record that information in AppendSearchDomains.
+ //
+ // We append search domains only for queries that are a single label. If overriden using
+ // command line argument "AlwaysAppendSearchDomains", then we do it for any query which
+ // is not fully qualified.
+
+ if ((rrtype == kDNSType_A || rrtype == kDNSType_AAAA) && name[strlen(name) - 1] != '.' &&
+ (AlwaysAppendSearchDomains || CountLabels(&q->qname) == 1))
+ {
+ q->AppendSearchDomains = 1;
+ q->AppendLocalSearchDomains = 1;
+ }
+ else
+ {
+ q->AppendSearchDomains = 0;
+ q->AppendLocalSearchDomains = 0;
+ }
+
+ // For single label queries that are not fully qualified, look at /etc/hosts, cache and try
+ // search domains before trying them on the wire as a single label query. RetryWithSearchDomains
+ // tell the core to call back into the UDS layer if there is no valid response in /etc/hosts or
+ // the cache
+ q->RetryWithSearchDomains = ApplySearchDomainsFirst(q) ? 1 : 0;
+ q->qnameOrig = mDNSNULL;
LogOperation("%3d: DNSServiceQueryRecord(%X, %d, %##s, %s) START", request->sd, flags, interfaceIndex, q->qname.c, DNSTypeName(q->qtype));
err = mDNS_StartQuery(&mDNSStorage, q);
else
{
request->terminate = queryrecord_termination_callback;
- if (q->InterfaceID == mDNSInterface_P2P || (!q->InterfaceID && SameDomainName((const domainname *)LastLabel(&q->qname), &localdomain)))
+ if (q->InterfaceID == mDNSInterface_P2P || (!q->InterfaceID && SameDomainName((const domainname *)LastLabel(&q->qname), &localdomain) && (flags & kDNSServiceFlagsIncludeP2P)))
+ {
+ LogInfo("handle_queryrecord_request: calling external_start_browsing_for_service()");
external_start_browsing_for_service(&mDNSStorage, &q->qname, q->qtype);
+ }
}
#if APPLE_OSX_mDNSResponder
- // Workaround for networks using Microsoft Active Directory using "local" as a private internal top-level domain
- extern domainname ActiveDirectoryPrimaryDomain;
- #define VALID_MSAD_SRV_TRANSPORT(T) (SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_tcp") || SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_udp"))
- #define VALID_MSAD_SRV(Q) ((Q)->qtype == kDNSType_SRV && VALID_MSAD_SRV_TRANSPORT(SecondLabel(&(Q)->qname)))
-
- if (!q->ForceMCast && SameDomainLabel(LastLabel(&q->qname), (const mDNSu8 *)&localdomain))
- if (q->qtype == kDNSType_A || q->qtype == kDNSType_AAAA || VALID_MSAD_SRV(q))
- {
- int labels = CountLabels(&q->qname);
- DNSQuestion *const q2 = &request->u.queryrecord.q2;
- *q2 = *q;
- q2->InterfaceID = mDNSInterface_Unicast;
- q2->ExpectUnique = mDNStrue;
-
- // For names of the form "<one-or-more-labels>.bar.local." we always do a second unicast query in parallel.
- // For names of the form "<one-label>.local." it's less clear whether we should do a unicast query.
- // If the name being queried is exactly the same as the name in the DHCP "domain" option (e.g. the DHCP
- // "domain" is my-small-company.local, and the user types "my-small-company.local" into their web browser)
- // then that's a hint that it's worth doing a unicast query. Otherwise, we first check to see if the
- // site's DNS server claims there's an SOA record for "local", and if so, that's also a hint that queries
- // for names in the "local" domain will be safely answered privately before they hit the root name servers.
- // Note that in the "my-small-company.local" example above there will typically be an SOA record for
- // "my-small-company.local" but *not* for "local", which is why the "local SOA" check would fail in that case.
- if (labels == 2 && !SameDomainName(&q->qname, &ActiveDirectoryPrimaryDomain))
- {
- AssignDomainName(&q2->qname, &localdomain);
- q2->qtype = kDNSType_SOA;
- q2->LongLived = mDNSfalse;
- q2->ForceMCast = mDNSfalse;
- q2->ReturnIntermed = mDNStrue;
- }
- LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast", request->sd, q2->qname.c, DNSTypeName(q2->qtype));
- err = mDNS_StartQuery(&mDNSStorage, q2);
- if (err) LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q2->qname.c, DNSTypeName(q2->qtype), (int)err);
- }
+ err = SendAdditionalQuery(q, request, err);
#endif // APPLE_OSX_mDNSResponder
return(err);
"%3d: DNSServiceReconfirmRecord(%s) interface %d initiated" :
"%3d: DNSServiceReconfirmRecord(%s) interface %d failed: %d",
request->sd, RRDisplayString(&mDNSStorage, &rr->resrec),
- mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, rr->resrec.InterfaceID), status);
+ mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, rr->resrec.InterfaceID, mDNSfalse), status);
freeL("AuthRecord/handle_reconfirm_request", rr);
}
return(status);
rep = create_reply(port_mapping_reply_op, replyLen, request);
rep->rhdr->flags = dnssd_htonl(0);
- rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, n->InterfaceID));
+ rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, n->InterfaceID, mDNSfalse));
rep->rhdr->error = dnssd_htonl(n->Result);
data = (char *)&rep->rhdr[1];
mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q4);
request->u.addrinfo.q4.QuestionContext = mDNSNULL;
}
+ if (request->u.addrinfo.q4.qnameOrig)
+ {
+ freeL("QueryTermination", request->u.addrinfo.q4.qnameOrig);
+ request->u.addrinfo.q4.qnameOrig = mDNSNULL;
+ }
+ if (request->u.addrinfo.q42)
+ {
+ if (request->u.addrinfo.q42->QuestionContext)
+ {
+ LogInfo("addrinfo_termination_callback: Stopping q42 %##s", request->u.addrinfo.q42->qname.c);
+ mDNS_StopQuery(&mDNSStorage, request->u.addrinfo.q42);
+ }
+ if (request->u.addrinfo.q42->qnameOrig)
+ {
+ LogInfo("addrinfo_termination_callback: freeing q42 qnameOrig %##s", request->u.addrinfo.q42->qnameOrig->c);
+ freeL("QueryTermination q42", request->u.addrinfo.q42->qnameOrig);
+ request->u.addrinfo.q42->qnameOrig = mDNSNULL;
+ }
+ freeL("addrinfo Q42", request->u.addrinfo.q42);
+ request->u.addrinfo.q42 = mDNSNULL;
+ }
if (request->u.addrinfo.q6.QuestionContext)
{
mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q6);
request->u.addrinfo.q6.QuestionContext = mDNSNULL;
}
+ if (request->u.addrinfo.q6.qnameOrig)
+ {
+ freeL("QueryTermination", request->u.addrinfo.q6.qnameOrig);
+ request->u.addrinfo.q6.qnameOrig = mDNSNULL;
+ }
+ if (request->u.addrinfo.q62)
+ {
+ if (request->u.addrinfo.q62->QuestionContext)
+ {
+ LogInfo("addrinfo_termination_callback: Stopping q62 %##s", request->u.addrinfo.q62->qname.c);
+ mDNS_StopQuery(&mDNSStorage, request->u.addrinfo.q62);
+ }
+ if (request->u.addrinfo.q62->qnameOrig)
+ {
+ LogInfo("addrinfo_termination_callback: freeing q62 qnameOrig %##s", request->u.addrinfo.q62->qnameOrig->c);
+ freeL("QueryTermination q62", request->u.addrinfo.q62->qnameOrig);
+ request->u.addrinfo.q62->qnameOrig = mDNSNULL;
+ }
+ freeL("addrinfo Q62", request->u.addrinfo.q62);
+ request->u.addrinfo.q62 = mDNSNULL;
+ }
}
mDNSlocal mStatus handle_addrinfo_request(request_state *request)
request->u.addrinfo.q4.ForceMCast = request->u.addrinfo.q6.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0;
request->u.addrinfo.q4.ReturnIntermed = request->u.addrinfo.q6.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
request->u.addrinfo.q4.SuppressUnusable = request->u.addrinfo.q6.SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable ) != 0;
- request->u.addrinfo.q4.SuppressUnusable = request->u.addrinfo.q6.SuppressUnusable = mDNSfalse;
+ request->u.addrinfo.q4.TimeoutQuestion = request->u.addrinfo.q6.TimeoutQuestion = (flags & kDNSServiceFlagsTimeout ) != 0;
+ request->u.addrinfo.q4.WakeOnResolve = request->u.addrinfo.q6.WakeOnResolve = 0;
+ request->u.addrinfo.q4.qnameOrig = request->u.addrinfo.q6.qnameOrig = mDNSNULL;
if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4)
{
request->u.addrinfo.q4.qtype = kDNSServiceType_A;
+ request->u.addrinfo.q4.SearchListIndex = 0;
+
+ // We append search domains only for queries that are a single label. If overriden using
+ // command line argument "AlwaysAppendSearchDomains", then we do it for any query which
+ // is not fully qualified.
+ if (hostname[strlen(hostname) - 1] != '.' && (AlwaysAppendSearchDomains || CountLabels(&d) == 1))
+ {
+ request->u.addrinfo.q4.AppendSearchDomains = 1;
+ request->u.addrinfo.q4.AppendLocalSearchDomains = 1;
+ }
+ else
+ {
+ request->u.addrinfo.q4.AppendSearchDomains = 0;
+ request->u.addrinfo.q4.AppendLocalSearchDomains = 0;
+ }
+ request->u.addrinfo.q4.RetryWithSearchDomains = (ApplySearchDomainsFirst(&request->u.addrinfo.q4) ? 1 : 0);
request->u.addrinfo.q4.QuestionCallback = queryrecord_result_callback;
request->u.addrinfo.q4.QuestionContext = request;
err = mDNS_StartQuery(&mDNSStorage, &request->u.addrinfo.q4);
LogMsg("ERROR: mDNS_StartQuery: %d", (int)err);
request->u.addrinfo.q4.QuestionContext = mDNSNULL;
}
+ #if APPLE_OSX_mDNSResponder
+ err = SendAdditionalQuery(&request->u.addrinfo.q4, request, err);
+ #endif // APPLE_OSX_mDNSResponder
}
if (!err && (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6))
{
request->u.addrinfo.q6.qtype = kDNSServiceType_AAAA;
+ request->u.addrinfo.q6.SearchListIndex = 0;
+ if (hostname[strlen(hostname) - 1] != '.' && (AlwaysAppendSearchDomains || CountLabels(&d) == 1))
+ {
+ request->u.addrinfo.q6.AppendSearchDomains = 1;
+ request->u.addrinfo.q6.AppendLocalSearchDomains = 1;
+ }
+ else
+ {
+ request->u.addrinfo.q6.AppendSearchDomains = 0;
+ request->u.addrinfo.q6.AppendLocalSearchDomains = 0;
+ }
+ request->u.addrinfo.q6.RetryWithSearchDomains = (ApplySearchDomainsFirst(&request->u.addrinfo.q6) ? 1 : 0);
request->u.addrinfo.q6.QuestionCallback = queryrecord_result_callback;
request->u.addrinfo.q6.QuestionContext = request;
err = mDNS_StartQuery(&mDNSStorage, &request->u.addrinfo.q6);
request->u.addrinfo.q4.QuestionContext = mDNSNULL;
}
}
+ #if APPLE_OSX_mDNSResponder
+ err = SendAdditionalQuery(&request->u.addrinfo.q6, request, err);
+ #endif // APPLE_OSX_mDNSResponder
}
LogOperation("%3d: DNSServiceGetAddrInfo(%X, %d, %d, %##s) START",
LogMsg("%3d: Got %d %d %d %d", req->sd, msg.msg_controllen, cmsg->cmsg_len, cmsg->cmsg_level, cmsg->cmsg_type);
#endif // DEBUG_64BIT_SCM_RIGHTS
if (msg.msg_controllen == sizeof(cbuf) &&
- cmsg->cmsg_len == sizeof(cbuf) &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(dnssd_sock_t)) &&
cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_RIGHTS)
{
(void)fd; // Unused
(void)filter; // Unused
- read_msg(req);
- if (req->ts == t_morecoming) return;
- if (req->ts == t_terminated || req->ts == t_error) { AbortUnlinkAndFree(req); return; }
- if (req->ts != t_complete) { LogMsg("req->ts %d != t_complete", req->ts); AbortUnlinkAndFree(req); return; }
-
- if (req->hdr.version != VERSION)
+ for (;;)
{
- LogMsg("ERROR: client version %d incompatible with daemon version %d", req->hdr.version, VERSION);
- AbortUnlinkAndFree(req);
- return;
- }
+ read_msg(req);
+ if (req->ts == t_morecoming) return;
+ if (req->ts == t_terminated || req->ts == t_error) { AbortUnlinkAndFree(req); return; }
+ if (req->ts != t_complete) { LogMsg("req->ts %d != t_complete", req->ts); AbortUnlinkAndFree(req); return; }
+
+ if (req->hdr.version != VERSION)
+ {
+ LogMsg("ERROR: client version %d incompatible with daemon version %d", req->hdr.version, VERSION);
+ AbortUnlinkAndFree(req);
+ return;
+ }
+
+ switch(req->hdr.op) // Interface + other data
+ {
+ case connection_request: min_size = 0; break;
+ case reg_service_request: min_size += sizeof(mDNSu32) + 4 /* name, type, domain, host */ + 4 /* port, textlen */; break;
+ case add_record_request: min_size += 4 /* type, rdlen */ + 4 /* ttl */; break;
+ case update_record_request: min_size += 2 /* rdlen */ + 4 /* ttl */; break;
+ case remove_record_request: break;
+ case browse_request: min_size += sizeof(mDNSu32) + 2 /* type, domain */; break;
+ case resolve_request: min_size += sizeof(mDNSu32) + 3 /* type, type, domain */; break;
+ case query_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 4 /* type, class*/; break;
+ case enumeration_request: min_size += sizeof(mDNSu32); break;
+ case reg_record_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */ + 4 /* ttl */; break;
+ case reconfirm_record_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */; break;
+ case setdomain_request: min_size += 1 /* domain */; break;
+ case getproperty_request: min_size = 2; break;
+ case port_mapping_request: min_size += sizeof(mDNSu32) + 4 /* udp/tcp */ + 4 /* int/ext port */ + 4 /* ttl */; break;
+ case addrinfo_request: min_size += sizeof(mDNSu32) + 4 /* v4/v6 */ + 1 /* hostname */; break;
+ case send_bpf: // Same as cancel_request below
+ case cancel_request: min_size = 0; break;
+ default: LogMsg("ERROR: validate_message - unsupported req type: %d", req->hdr.op); min_size = -1; break;
+ }
+
+ if ((mDNSs32)req->data_bytes < min_size)
+ { LogMsg("Invalid message %d bytes; min for %d is %d", req->data_bytes, req->hdr.op, min_size); AbortUnlinkAndFree(req); return; }
+
+ if (LightweightOp(req->hdr.op) && !req->terminate)
+ { LogMsg("Reg/Add/Update/Remove %d require existing connection", req->hdr.op); AbortUnlinkAndFree(req); return; }
+
+ // check if client wants silent operation
+ if (req->hdr.ipc_flags & IPC_FLAGS_NOREPLY) req->no_reply = 1;
+
+ // If req->terminate is already set, this means this operation is sharing an existing connection
+ if (req->terminate && !LightweightOp(req->hdr.op))
+ {
+ request_state *newreq = NewRequest();
+ newreq->primary = req;
+ newreq->sd = req->sd;
+ newreq->errsd = req->errsd;
+ newreq->uid = req->uid;
+ newreq->hdr = req->hdr;
+ newreq->msgbuf = req->msgbuf;
+ newreq->msgptr = req->msgptr;
+ newreq->msgend = req->msgend;
+ req = newreq;
+ }
+
+ // If we're shutting down, don't allow new client requests
+ // We do allow "cancel" and "getproperty" during shutdown
+ if (mDNSStorage.ShutdownTime && req->hdr.op != cancel_request && req->hdr.op != getproperty_request)
+ {
+ err = mStatus_ServiceNotRunning;
+ }
+ else switch(req->hdr.op)
+ {
+ // These are all operations that have their own first-class request_state object
+ case connection_request: LogOperation("%3d: DNSServiceCreateConnection START", req->sd);
+ req->terminate = connection_termination; break;
+ case resolve_request: err = handle_resolve_request (req); break;
+ case query_request: err = handle_queryrecord_request (req); break;
+ case browse_request: err = handle_browse_request (req); break;
+ case reg_service_request: err = handle_regservice_request (req); break;
+ case enumeration_request: err = handle_enum_request (req); break;
+ case reconfirm_record_request: err = handle_reconfirm_request (req); break;
+ case setdomain_request: err = handle_setdomain_request (req); break;
+ case getproperty_request: handle_getproperty_request (req); break;
+ case port_mapping_request: err = handle_port_mapping_request(req); break;
+ case addrinfo_request: err = handle_addrinfo_request (req); break;
+ case send_bpf: /* Do nothing for send_bpf */ break;
+
+ // These are all operations that work with an existing request_state object
+ case reg_record_request: err = handle_regrecord_request (req); break;
+ case add_record_request: err = handle_add_request (req); break;
+ case update_record_request: err = handle_update_request (req); break;
+ case remove_record_request: err = handle_removerecord_request(req); break;
+ case cancel_request: handle_cancel_request (req); break;
+ default: LogMsg("%3d: ERROR: Unsupported UDS req: %d", req->sd, req->hdr.op);
+ }
- switch(req->hdr.op) // Interface + other data
- {
- case connection_request: min_size = 0; break;
- case reg_service_request: min_size += sizeof(mDNSu32) + 4 /* name, type, domain, host */ + 4 /* port, textlen */; break;
- case add_record_request: min_size += 4 /* type, rdlen */ + 4 /* ttl */; break;
- case update_record_request: min_size += 2 /* rdlen */ + 4 /* ttl */; break;
- case remove_record_request: break;
- case browse_request: min_size += sizeof(mDNSu32) + 2 /* type, domain */; break;
- case resolve_request: min_size += sizeof(mDNSu32) + 3 /* type, type, domain */; break;
- case query_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 4 /* type, class*/; break;
- case enumeration_request: min_size += sizeof(mDNSu32); break;
- case reg_record_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */ + 4 /* ttl */; break;
- case reconfirm_record_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */; break;
- case setdomain_request: min_size += 1 /* domain */; break;
- case getproperty_request: min_size = 2; break;
- case port_mapping_request: min_size += sizeof(mDNSu32) + 4 /* udp/tcp */ + 4 /* int/ext port */ + 4 /* ttl */; break;
- case addrinfo_request: min_size += sizeof(mDNSu32) + 4 /* v4/v6 */ + 1 /* hostname */; break;
- case send_bpf: // Same as cancel_request below
- case cancel_request: min_size = 0; break;
- default: LogMsg("ERROR: validate_message - unsupported req type: %d", req->hdr.op); min_size = -1; break;
- }
-
- if ((mDNSs32)req->data_bytes < min_size)
- { LogMsg("Invalid message %d bytes; min for %d is %d", req->data_bytes, req->hdr.op, min_size); AbortUnlinkAndFree(req); return; }
-
- if (LightweightOp(req->hdr.op) && !req->terminate)
- { LogMsg("Reg/Add/Update/Remove %d require existing connection", req->hdr.op); AbortUnlinkAndFree(req); return; }
-
- // check if client wants silent operation
- if (req->hdr.ipc_flags & IPC_FLAGS_NOREPLY) req->no_reply = 1;
-
- // If req->terminate is already set, this means this operation is sharing an existing connection
- if (req->terminate && !LightweightOp(req->hdr.op))
- {
- request_state *newreq = NewRequest();
- newreq->primary = req;
- newreq->sd = req->sd;
- newreq->errsd = req->errsd;
- newreq->uid = req->uid;
- newreq->hdr = req->hdr;
- newreq->msgbuf = req->msgbuf;
- newreq->msgptr = req->msgptr;
- newreq->msgend = req->msgend;
- req = newreq;
- }
-
- // If we're shutting down, don't allow new client requests
- // We do allow "cancel" and "getproperty" during shutdown
- if (mDNSStorage.ShutdownTime && req->hdr.op != cancel_request && req->hdr.op != getproperty_request)
- {
- err = mStatus_ServiceNotRunning;
- }
- else switch(req->hdr.op)
- {
- // These are all operations that have their own first-class request_state object
- case connection_request: LogOperation("%3d: DNSServiceCreateConnection START", req->sd);
- req->terminate = connection_termination; break;
- case resolve_request: err = handle_resolve_request (req); break;
- case query_request: err = handle_queryrecord_request (req); break;
- case browse_request: err = handle_browse_request (req); break;
- case reg_service_request: err = handle_regservice_request (req); break;
- case enumeration_request: err = handle_enum_request (req); break;
- case reconfirm_record_request: err = handle_reconfirm_request (req); break;
- case setdomain_request: err = handle_setdomain_request (req); break;
- case getproperty_request: handle_getproperty_request (req); break;
- case port_mapping_request: err = handle_port_mapping_request(req); break;
- case addrinfo_request: err = handle_addrinfo_request (req); break;
- case send_bpf: /* Do nothing for send_bpf */ break;
-
- // These are all operations that work with an existing request_state object
- case reg_record_request: err = handle_regrecord_request (req); break;
- case add_record_request: err = handle_add_request (req); break;
- case update_record_request: err = handle_update_request (req); break;
- case remove_record_request: err = handle_removerecord_request(req); break;
- case cancel_request: handle_cancel_request (req); break;
- default: LogMsg("%3d: ERROR: Unsupported UDS req: %d", req->sd, req->hdr.op);
- }
-
- // req->msgbuf may be NULL, e.g. for connection_request or remove_record_request
- if (req->msgbuf) freeL("request_state msgbuf", req->msgbuf);
-
- // There's no return data for a cancel request (DNSServiceRefDeallocate returns no result)
- // For a DNSServiceGetProperty call, the handler already generated the response, so no need to do it again here
- if (req->hdr.op != cancel_request && req->hdr.op != getproperty_request && req->hdr.op != send_bpf)
- {
- const mStatus err_netorder = dnssd_htonl(err);
- send_all(req->errsd, (const char *)&err_netorder, sizeof(err_netorder));
- if (req->errsd != req->sd)
- {
- LogOperation("%3d: Error socket %d closed %08X %08X (%d)",
- req->sd, req->errsd, req->hdr.client_context.u32[1], req->hdr.client_context.u32[0], err);
- dnssd_close(req->errsd);
- req->errsd = req->sd;
- // Also need to reset the parent's errsd, if this is a subordinate operation
- if (req->primary) req->primary->errsd = req->primary->sd;
- }
- }
-
- // Reset ready to accept the next req on this pipe
- if (req->primary) req = req->primary;
- req->ts = t_morecoming;
- req->hdr_bytes = 0;
- req->data_bytes = 0;
- req->msgbuf = mDNSNULL;
- req->msgptr = mDNSNULL;
- req->msgend = 0;
+ // req->msgbuf may be NULL, e.g. for connection_request or remove_record_request
+ if (req->msgbuf) freeL("request_state msgbuf", req->msgbuf);
+
+ // There's no return data for a cancel request (DNSServiceRefDeallocate returns no result)
+ // For a DNSServiceGetProperty call, the handler already generated the response, so no need to do it again here
+ if (req->hdr.op != cancel_request && req->hdr.op != getproperty_request && req->hdr.op != send_bpf)
+ {
+ const mStatus err_netorder = dnssd_htonl(err);
+ send_all(req->errsd, (const char *)&err_netorder, sizeof(err_netorder));
+ if (req->errsd != req->sd)
+ {
+ LogOperation("%3d: Error socket %d closed %08X %08X (%d)",
+ req->sd, req->errsd, req->hdr.client_context.u32[1], req->hdr.client_context.u32[0], err);
+ dnssd_close(req->errsd);
+ req->errsd = req->sd;
+ // Also need to reset the parent's errsd, if this is a subordinate operation
+ if (req->primary) req->primary->errsd = req->primary->sd;
+ }
+ }
+
+ // Reset ready to accept the next req on this pipe
+ if (req->primary) req = req->primary;
+ req->ts = t_morecoming;
+ req->hdr_bytes = 0;
+ req->data_bytes = 0;
+ req->msgbuf = mDNSNULL;
+ req->msgptr = mDNSNULL;
+ req->msgend = 0;
+ }
}
mDNSlocal void connect_callback(int fd, short filter, void *info)
LogMsgNoIdent("%s Unrecognized operation %p", prefix, req->terminate);
}
+mDNSlocal char *RecordTypeName(mDNSu8 rtype)
+ {
+ switch (rtype)
+ {
+ case kDNSRecordTypeUnregistered: return ("Unregistered ");
+ case kDNSRecordTypeDeregistering: return ("Deregistering");
+ case kDNSRecordTypeUnique: return ("Unique ");
+ case kDNSRecordTypeAdvisory: return ("Advisory ");
+ case kDNSRecordTypeShared: return ("Shared ");
+ case kDNSRecordTypeVerified: return ("Verified ");
+ case kDNSRecordTypeKnownUnique: return ("KnownUnique ");
+ default: return("Unknown");
+ }
+ }
+
+mDNSlocal void LogEtcHosts(mDNS *const m)
+ {
+ mDNSBool showheader = mDNStrue;
+ const AuthRecord *ar;
+ mDNSu32 slot;
+ AuthGroup *ag;
+ int count = 0;
+ int authslot = 0;
+ mDNSBool truncated = 0;
+
+ for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+ {
+ if (m->rrauth.rrauth_hash[slot]) authslot++;
+ for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next)
+ for (ar = ag->members; ar; ar = ar->next)
+ {
+ if (ar->RecordCallback != FreeEtcHosts) continue;
+ if (showheader) { showheader = mDNSfalse; LogMsgNoIdent(" State Interface"); }
+
+ // Print a maximum of 50 records
+ if (count++ >= 50) { truncated = mDNStrue; continue; }
+ if (ar->ARType == AuthRecordLocalOnly)
+ {
+ if (ar->resrec.InterfaceID == mDNSInterface_LocalOnly)
+ LogMsgNoIdent(" %s LO %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar));
+ else
+ {
+ mDNSu32 scopeid = (mDNSu32)(uintptr_t)ar->resrec.InterfaceID;
+ LogMsgNoIdent(" %s %u %s", RecordTypeName(ar->resrec.RecordType), scopeid, ARDisplayString(m, ar));
+ }
+ }
+ usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
+ }
+ }
+
+ if (showheader) LogMsgNoIdent("<None>");
+ else if (truncated) LogMsgNoIdent("<Truncated: to 50 records, Total records %d, Total Auth Groups %d, Auth Slots %d>", count, m->rrauth.rrauth_totalused, authslot);
+ }
+
+mDNSlocal void LogLocalOnlyAuthRecords(mDNS *const m)
+ {
+ mDNSBool showheader = mDNStrue;
+ const AuthRecord *ar;
+ mDNSu32 slot;
+ AuthGroup *ag;
+
+ for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+ {
+ for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next)
+ for (ar = ag->members; ar; ar = ar->next)
+ {
+ if (ar->RecordCallback == FreeEtcHosts) continue;
+ if (showheader) { showheader = mDNSfalse; LogMsgNoIdent(" State Interface"); }
+
+ // Print a maximum of 400 records
+ if (ar->ARType == AuthRecordLocalOnly)
+ LogMsgNoIdent(" %s LO %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar));
+ else if (ar->ARType == AuthRecordP2P)
+ LogMsgNoIdent(" %s PP %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar));
+ usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
+ }
+ }
+
+ if (showheader) LogMsgNoIdent("<None>");
+ }
+
mDNSlocal void LogAuthRecords(mDNS *const m, const mDNSs32 now, AuthRecord *ResourceRecords, int *proxy)
{
mDNSBool showheader = mDNStrue;
(ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond,
ar->expire ? (ar->expire - now) / mDNSPlatformOneSecond : 0,
ar->state, ARDisplayString(m, ar));
- else if (ar->resrec.InterfaceID == mDNSInterface_LocalOnly)
+ else if (ar->ARType == AuthRecordLocalOnly)
LogMsgNoIdent(" LO %s", ARDisplayString(m, ar));
- else if (ar->resrec.InterfaceID == mDNSInterface_P2P)
+ else if (ar->ARType == AuthRecordP2P)
LogMsgNoIdent(" PP %s", ARDisplayString(m, ar));
else
LogMsgNoIdent("%7d %7d %7d %7s %s",
LogMsgNoIdent("--------- Auth Records ---------");
LogAuthRecords(m, now, m->ResourceRecords, mDNSNULL);
+ LogMsgNoIdent("--------- LocalOnly, P2P Auth Records ---------");
+ LogLocalOnlyAuthRecords(m);
+
+ LogMsgNoIdent("--------- /etc/hosts ---------");
+ LogEtcHosts(m);
+
LogMsgNoIdent("------ Duplicate Records -------");
LogAuthRecords(m, now, m->DuplicateRecords, mDNSNULL);
{
const DomainAuthInfo *a;
for (a = m->AuthInfoList; a; a = a->next)
- LogMsgNoIdent("%##s %##s%s", a->domain.c, a->keyname.c, a->AutoTunnel ? " AutoTunnel" : "");
+ LogMsgNoIdent("%##s %##s %##s %d %s", a->domain.c, a->keyname.c, a->hostname.c, (a->port.b[0] << 8 | a->port.b[1]), a->AutoTunnel ? a->AutoTunnel : "");
}
#if APPLE_OSX_mDNSResponder
{
const ClientTunnel *c;
for (c = m->TunnelClients; c; c = c->next)
- LogMsgNoIdent("%##s local %.16a %.4a %.16a remote %.16a %.4a %5d %.16a interval %d",
- c->dstname.c, &c->loc_inner, &c->loc_outer, &c->loc_outer6, &c->rmt_inner, &c->rmt_outer, mDNSVal16(c->rmt_outer_port), &c->rmt_outer6, c->q.ThisQInterval);
+ LogMsgNoIdent("%s %##s local %.16a %.4a %.16a remote %.16a %.4a %5d %.16a interval %d",
+ c->prefix, c->dstname.c, &c->loc_inner, &c->loc_outer, &c->loc_outer6, &c->rmt_inner, &c->rmt_outer, mDNSVal16(c->rmt_outer_port), &c->rmt_outer6, c->q.ThisQInterval);
}
#endif // APPLE_OSX_mDNSResponder
if (!AutoRegistrationDomains) LogMsgNoIdent("<None>");
else for (d=AutoRegistrationDomains; d; d=d->next) LogMsgNoIdent("%##s", d->name.c);
- LogMsgNoIdent("--- Search Domains --");
- if (!SearchList) LogMsgNoIdent("<None>");
- else
- {
- for (s=SearchList; s; s=s->next)
- {
- LogMsgNoIdent("%##s", s->domain.c);
- }
- }
+ LogMsgNoIdent("--- Search Domains --");
+ if (!SearchList) LogMsgNoIdent("<None>");
+ else
+ {
+ for (s=SearchList; s; s=s->next)
+ {
+ char *ifname = InterfaceNameForID(m, s->InterfaceID);
+ LogMsgNoIdent("%##s %s", s->domain.c, ifname ? ifname : "");
+ }
+ }
+
LogMsgNoIdent("---- Task Scheduling Timers ----");
if (!m->NewQuestions)
LogMsgNoIdent("SPSProxyListChanged%s", m->SPSProxyListChanged ? "" : " <NONE>");
LogMsgNoIdent("LocalRemoveEvents%s", m->LocalRemoveEvents ? "" : " <NONE>");
+ LogMsgNoIdent("m->RegisterAutoTunnel6 %08X", m->RegisterAutoTunnel6);
+ LogMsgNoIdent("m->AutoTunnelRelayAddrIn %.16a", &m->AutoTunnelRelayAddrIn);
+ LogMsgNoIdent("m->AutoTunnelRelayAddrOut %.16a", &m->AutoTunnelRelayAddrOut);
#define LogTimer(MSG,T) LogMsgNoIdent( MSG " %08X %11d %08X %11d", (T), (T), (T)-now, (T)-now)
LogTimer("m->ProbeFailTime ", m->ProbeFailTime);
LogTimer("m->DelaySleep ", m->DelaySleep);
LogTimer("m->SleepLimit ", m->SleepLimit);
- LogMsgNoIdent("m->RegisterAutoTunnel6 %08X", m->RegisterAutoTunnel6);
- LogMsgNoIdent("m->AutoTunnelRelayAddrIn %.16a", &m->AutoTunnelRelayAddrIn);
- LogMsgNoIdent("m->AutoTunnelRelayAddrOut %.16a", &m->AutoTunnelRelayAddrOut);
+ LogTimer("m->NextScheduledStopTime ", m->NextScheduledStopTime);
}
#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING
// 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
// cause structure sizes (and therefore memory usage) to balloon unreasonably.
- char sizecheck_request_state [(sizeof(request_state) <= 2000) ? 1 : -1];
+ char sizecheck_request_state [(sizeof(request_state) <= 1784) ? 1 : -1];
char sizecheck_registered_record_entry[(sizeof(registered_record_entry) <= 60) ? 1 : -1];
char sizecheck_service_instance [(sizeof(service_instance) <= 6552) ? 1 : -1];
- char sizecheck_browser_t [(sizeof(browser_t) <= 1026) ? 1 : -1];
+ char sizecheck_browser_t [(sizeof(browser_t) <= 1050) ? 1 : -1];
char sizecheck_reply_hdr [(sizeof(reply_hdr) <= 12) ? 1 : -1];
char sizecheck_reply_state [(sizeof(reply_state) <= 64) ? 1 : -1];
};
extern void machserver_automatic_browse_domain_changed(const domainname *d, mDNSBool add);
extern void machserver_automatic_registration_domain_changed(const domainname *d, mDNSBool add);
// External support
+extern void mDNSInitPacketFilter(void);
extern void external_start_browsing_for_service(mDNS *const m, const domainname *const type, DNS_TypeValues qtype);
extern void external_stop_browsing_for_service(mDNS *const m, const domainname *const type, DNS_TypeValues qtype);
extern void external_start_advertising_service(const ResourceRecord *const resourceRecord);
BufferSecurityCheck="true"\r
UsePrecompiledHeader="0"\r
AssemblerListingLocation="$(IntDir)\"\r
+ ProgramDataBaseFileName="$(IntDir)\dnssd.dll.pdb"\r
WarningLevel="4"\r
Detect64BitPortabilityProblems="true"\r
DebugInformationFormat="3"\r
LinkIncremental="2"\r
ModuleDefinitionFile="dnssd.def"\r
GenerateDebugInformation="true"\r
- ProgramDatabaseFile="$(OutDir)/dnssd.pdb"\r
+ ProgramDatabaseFile="$(OutDir)\dnssd.dll.pdb"\r
SubSystem="2"\r
BaseAddress="0x16000000"\r
ImportLibrary="$(OutDir)\dnssd.lib"\r
BufferSecurityCheck="true"\r
UsePrecompiledHeader="0"\r
AssemblerListingLocation="$(IntDir)\"\r
+ ProgramDataBaseFileName="$(IntDir)\dnssd.dll.pdb"\r
WarningLevel="4"\r
Detect64BitPortabilityProblems="true"\r
DebugInformationFormat="3"\r
LinkIncremental="2"\r
ModuleDefinitionFile="dnssd.def"\r
GenerateDebugInformation="true"\r
- ProgramDatabaseFile="$(OutDir)/dnssd.pdb"\r
+ ProgramDatabaseFile="$(OutDir)\dnssd.dll.pdb"\r
SubSystem="2"\r
BaseAddress="0x16000000"\r
ImportLibrary="$(OutDir)\dnssd.lib"\r
RuntimeLibrary="0"\r
UsePrecompiledHeader="0"\r
AssemblerListingLocation="$(IntDir)\"\r
+ ProgramDataBaseFileName="$(IntDir)\dnssd.dll.pdb"\r
WarningLevel="4"\r
Detect64BitPortabilityProblems="true"\r
DebugInformationFormat="3"\r
LinkIncremental="1"\r
ModuleDefinitionFile="dnssd.def"\r
GenerateDebugInformation="true"\r
- ProgramDatabaseFile="$(IntDir)/$(ProjectName).pdb"\r
+ ProgramDatabaseFile="$(OutDir)\dnssd.dll.pdb"\r
SubSystem="2"\r
OptimizeReferences="2"\r
EnableCOMDATFolding="2"\r
/>\r
<Tool\r
Name="VCPostBuildEventTool"\r
- CommandLine="if not "%RC_XBS%" == "YES" goto END
if not exist "$(DSTROOT)\WINDOWS\system32\$(PlatformName)" mkdir "$(DSTROOT)\WINDOWS\system32\$(PlatformName)"
if not exist "$(DSTROOT)\Program Files\Bonjour SDK\include" mkdir "$(DSTROOT)\Program Files\Bonjour SDK\include"
xcopy /I/Y "$(TargetPath)" "$(DSTROOT)\WINDOWS\system32\$(PlatformName)"
xcopy /I/Y "$(ProjectDir)..\..\mDNSShared\dns_sd.h" "$(DSTROOT)\Program Files\Bonjour SDK\include"
:END
"\r
+ CommandLine="if not "%RC_XBS%" == "YES" goto END
if not exist "$(DSTROOT)\WINDOWS\system32\$(PlatformName)" mkdir "$(DSTROOT)\WINDOWS\system32\$(PlatformName)"
if not exist "$(DSTROOT)\Program Files\Bonjour SDK\lib" mkdir "$(DSTROOT)\Program Files\Bonjour SDK\lib"
if not exist "$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)" mkdir "$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)"
if not exist "$(DSTROOT)\Program Files\Bonjour SDK\include" mkdir "$(DSTROOT)\Program Files\Bonjour SDK\include"
if not exist "$(DSTROOT)\AppleInternal" mkdir "$(DSTROOT)\AppleInternal"
if not exist "$(DSTROOT)\AppleInternal\bin" mkdir "$(DSTROOT)\AppleInternal\bin"
if not exist "$(DSTROOT)\AppleInternal\bin\$(PlatformName)" mkdir "$(DSTROOT)\AppleInternal\bin\$(PlatformName)"
xcopy /I/Y "$(TargetPath)" "$(DSTROOT)\WINDOWS\system32\$(PlatformName)"
xcopy /I/Y "$(OutDir)\dnssd.dll.pdb" "$(DSTROOT)\AppleInternal\bin\$(PlatformName)"
xcopy /I/Y "$(ProjectDir)..\..\mDNSShared\dns_sd.h" "$(DSTROOT)\Program Files\Bonjour SDK\include"
:END
"\r
/>\r
</Configuration>\r
<Configuration\r
RuntimeLibrary="0"\r
UsePrecompiledHeader="0"\r
AssemblerListingLocation="$(IntDir)\"\r
+ ProgramDataBaseFileName="$(IntDir)\dnssd.dll.pdb"\r
WarningLevel="4"\r
Detect64BitPortabilityProblems="true"\r
DebugInformationFormat="3"\r
LinkIncremental="1"\r
ModuleDefinitionFile="dnssd.def"\r
GenerateDebugInformation="true"\r
- ProgramDatabaseFile="$(IntDir)/$(ProjectName).pdb"\r
+ ProgramDatabaseFile="$(OutDir)\dnssd.dll.pdb"\r
SubSystem="2"\r
OptimizeReferences="2"\r
EnableCOMDATFolding="2"\r
/>\r
<Tool\r
Name="VCPostBuildEventTool"\r
- CommandLine="if not "%RC_XBS%" == "YES" goto END
if not exist "$(DSTROOT)\WINDOWS\system32\$(PlatformName)" mkdir "$(DSTROOT)\WINDOWS\system32\$(PlatformName)"
if not exist "$(DSTROOT)\Program Files\Bonjour SDK\include" mkdir "$(DSTROOT)\Program Files\Bonjour SDK\include"
xcopy /I/Y "$(TargetPath)" "$(DSTROOT)\WINDOWS\system32\$(PlatformName)"
:END
"\r
+ CommandLine="if not "%RC_XBS%" == "YES" goto END
if not exist "$(DSTROOT)\WINDOWS\system32\$(PlatformName)" mkdir "$(DSTROOT)\WINDOWS\system32\$(PlatformName)"
if not exist "$(DSTROOT)\Program Files\Bonjour SDK\lib" mkdir "$(DSTROOT)\Program Files\Bonjour SDK\lib"
if not exist "$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)" mkdir "$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)"
if not exist "$(DSTROOT)\Program Files\Bonjour SDK\include" mkdir "$(DSTROOT)\Program Files\Bonjour SDK\include"
if not exist "$(DSTROOT)\AppleInternal" mkdir "$(DSTROOT)\AppleInternal"
if not exist "$(DSTROOT)\AppleInternal\bin" mkdir "$(DSTROOT)\AppleInternal\bin"
if not exist "$(DSTROOT)\AppleInternal\bin\$(PlatformName)" mkdir "$(DSTROOT)\AppleInternal\bin\$(PlatformName)"
xcopy /I/Y "$(TargetPath)" "$(DSTROOT)\WINDOWS\system32\$(PlatformName)"
xcopy /I/Y "$(OutDir)\dnssd.dll.pdb" "$(DSTROOT)\AppleInternal\bin\$(PlatformName)"
:END
"\r
/>\r
</Configuration>\r
</Configurations>\r
BasicRuntimeChecks="3"\r
RuntimeLibrary="1"\r
UsePrecompiledHeader="0"\r
+ ProgramDataBaseFileName="$(IntDir)\dnssd.pdb"\r
WarningLevel="3"\r
Detect64BitPortabilityProblems="true"\r
DebugInformationFormat="3"\r
BasicRuntimeChecks="3"\r
RuntimeLibrary="1"\r
UsePrecompiledHeader="0"\r
+ ProgramDataBaseFileName="$(IntDir)\dnssd.pdb"\r
WarningLevel="3"\r
Detect64BitPortabilityProblems="true"\r
DebugInformationFormat="3"\r
PreprocessorDefinitions="WIN32;NDEBUG;_LIB;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1"\r
RuntimeLibrary="0"\r
UsePrecompiledHeader="0"\r
+ ProgramDataBaseFileName="$(IntDir)\dnssd.lib.pdb"\r
WarningLevel="3"\r
Detect64BitPortabilityProblems="true"\r
DebugInformationFormat="3"\r
/>\r
<Tool\r
Name="VCPostBuildEventTool"\r
- CommandLine="if not "%RC_XBS%" == "YES" goto END
if not exist "$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)" mkdir "$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)"
echo F | xcopy /Y "$(OutDir)\dnssdStatic.lib" "$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)\dnssd.lib"
:END
"\r
+ CommandLine="if not "%RC_XBS%" == "YES" goto END
if not exist "$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)" mkdir "$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)"
echo F | xcopy /Y "$(OutDir)\dnssdStatic.lib" "$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)\dnssd.lib"
xcopy /Y "$(OutDir)\dnssd.lib.pdb" "$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)"
:END
"\r
/>\r
</Configuration>\r
<Configuration\r
PreprocessorDefinitions="WIN32;NDEBUG;_LIB;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1"\r
RuntimeLibrary="0"\r
UsePrecompiledHeader="0"\r
+ ProgramDataBaseFileName="$(IntDir)\dnssd.lib.pdb"\r
WarningLevel="3"\r
Detect64BitPortabilityProblems="true"\r
DebugInformationFormat="3"\r
/>\r
<Tool\r
Name="VCPostBuildEventTool"\r
- CommandLine="if not "%RC_XBS%" == "YES" goto END
if not exist "$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)" mkdir "$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)"
echo F | xcopy /I/Y "$(OutDir)\dnssdStatic.lib" "$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)\dnssd.lib"
:END
"\r
+ CommandLine="if not "%RC_XBS%" == "YES" goto END
if not exist "$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)" mkdir "$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)"
echo F | xcopy /I/Y "$(OutDir)\dnssdStatic.lib" "$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)\dnssd.lib"
xcopy /Y "$(OutDir)\dnssd.lib.pdb" "$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)"
:END
"\r
/>\r
</Configuration>\r
</Configurations>\r
gPlatformStorage.registerWaitableEventFunc = RegisterWaitableEvent;
gPlatformStorage.unregisterWaitableEventFunc = UnregisterWaitableEvent;
+ gPlatformStorage.reportStatusFunc = ReportStatus;
err = mDNS_Init( &gMDNSRecord, &gPlatformStorage, gRRCache, RR_CACHE_SIZE, mDNS_Init_AdvertiseLocalAddresses, CoreCallback, mDNS_Init_NoInitCallbackContext);
require_noerr( err, exit);
}
// Wait until something occurs (e.g. cancel, incoming packet, or timeout).
+ //
+ // Note: There seems to be a bug in WinSock with respect to Alertable I/O. According
+ // to MSDN <http://msdn.microsoft.com/en-us/library/aa363772(VS.85).aspx>, Alertable I/O
+ // callbacks will only be invoked during the following calls (when the caller sets
+ // the appropriate flag):
+ //
+ // - SleepEx
+ // - WaitForSingleObjectEx
+ // - WaitForMultipleObjectsEx
+ // - SignalObjectAndWait
+ // - MsgWaitForMultipleObjectsEx
+ //
+ // However, we have seen callbacks be invoked during calls to bind() (and maybe others). If there
+ // weren't a bug, then socket events would only be queued during the call to WaitForMultipleObjects() and
+ // we'd only have to check them once afterwards. However since that doesn't seem to be the case, we'll
+ // check the queue both before we call WaitForMultipleObjects() and after.
- SetSocketEventsEnabled( &gMDNSRecord, TRUE );
+ DispatchSocketEvents( &gMDNSRecord );
result = WaitForMultipleObjectsEx( ( DWORD ) waitListCount, waitList, FALSE, (DWORD) nextTimerEvent, TRUE );
- SetSocketEventsEnabled( &gMDNSRecord, FALSE );
check( result != WAIT_FAILED );
+ DispatchSocketEvents( &gMDNSRecord );
if ( result != WAIT_FAILED )
{
// udsSupportAddFDToEventLoop
//===========================================================================================================================
+
mStatus
udsSupportAddFDToEventLoop( SocketRef fd, udsEventCallback callback, void *context, void **platform_data)
{
<Tool\r
Name="VCCLCompilerTool"\r
Optimization="0"\r
- AdditionalIncludeDirectories=".;../;../../mDNSCore;../../mDNSShared;"C:/Program Files/Microsoft SDKs/Windows/v6.1/Include""\r
+ AdditionalIncludeDirectories=".;../;../../mDNSCore;../../mDNSShared;"$(VCInstallDir)include";"$(VCInstallDir)atlmfc\include";"C:/Program Files/Microsoft SDKs/Windows/v6.1/Include""\r
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;_WIN32_WINNT=0x0501;DEBUG=1;MDNS_DEBUGMSGS=0;TARGET_OS_WIN32;WIN32_LEAN_AND_MEAN;USE_TCP_LOOPBACK;PLATFORM_NO_STRSEP;PLATFORM_NO_EPIPE;PLATFORM_NO_RLIMIT;PID_FILE="""";UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1;_LEGACY_NAT_TRAVERSAL_;_USE_32BIT_TIME_T"\r
StringPooling="true"\r
MinimalRebuild="true"\r
<Tool\r
Name="VCCLCompilerTool"\r
Optimization="0"\r
- AdditionalIncludeDirectories=".;../;../../mDNSCore;../../mDNSShared;"C:/Program Files/Microsoft SDKs/Windows/v6.1/Include""\r
+ AdditionalIncludeDirectories=".;../;../../mDNSCore;../../mDNSShared;"$(VCInstallDir)include";"$(VCInstallDir)atlmfc\include";"C:/Program Files/Microsoft SDKs/Windows/v6.1/Include""\r
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;_WIN32_WINNT=0x0501;DEBUG=1;MDNS_DEBUGMSGS=0;TARGET_OS_WIN32;WIN32_LEAN_AND_MEAN;USE_TCP_LOOPBACK;PLATFORM_NO_STRSEP;PLATFORM_NO_EPIPE;PLATFORM_NO_RLIMIT;PID_FILE="""";UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1;_LEGACY_NAT_TRAVERSAL_"\r
StringPooling="true"\r
MinimalRebuild="true"\r
/>\r
<Tool\r
Name="VCCLCompilerTool"\r
- AdditionalIncludeDirectories=".;../;../../mDNSCore;../../mDNSShared;"C:/Program Files/Microsoft SDKs/Windows/v6.1/Include""\r
+ AdditionalIncludeDirectories=".;../;../../mDNSCore;../../mDNSShared;"$(VCInstallDir)include";"$(VCInstallDir)atlmfc\include";"C:/Program Files/Microsoft SDKs/Windows/v6.1/Include""\r
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_WIN32_WINNT=0x0501;TARGET_OS_WIN32;WIN32_LEAN_AND_MEAN;USE_TCP_LOOPBACK;PLATFORM_NO_STRSEP;PLATFORM_NO_EPIPE;PLATFORM_NO_RLIMIT;PID_FILE="""";UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1;_LEGACY_NAT_TRAVERSAL_;_USE_32BIT_TIME_T"\r
RuntimeLibrary="0"\r
UsePrecompiledHeader="0"\r
/>\r
<Tool\r
Name="VCCLCompilerTool"\r
- AdditionalIncludeDirectories=".;../;../../mDNSCore;../../mDNSShared;"C:/Program Files/Microsoft SDKs/Windows/v6.1/Include""\r
+ AdditionalIncludeDirectories=".;../;../../mDNSCore;../../mDNSShared;"$(VCInstallDir)include";"$(VCInstallDir)atlmfc\include";"C:/Program Files/Microsoft SDKs/Windows/v6.1/Include""\r
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_WIN32_WINNT=0x0501;TARGET_OS_WIN32;WIN32_LEAN_AND_MEAN;USE_TCP_LOOPBACK;PLATFORM_NO_STRSEP;PLATFORM_NO_EPIPE;PLATFORM_NO_RLIMIT;PID_FILE="""";UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1;_LEGACY_NAT_TRAVERSAL_"\r
RuntimeLibrary="0"\r
UsePrecompiledHeader="0"\r
mDNSlocal void TCPCanRead( TCPSocket * sock );
mDNSlocal mStatus TCPBeginRecv( TCPSocket * sock );
mDNSlocal void CALLBACK TCPEndRecv( DWORD error, DWORD bytesTransferred, LPWSAOVERLAPPED overlapped, DWORD flags );
+mDNSlocal void TCPCloseSocket( TCPSocket * socket );
mDNSlocal void CALLBACK TCPFreeSocket( TCPSocket *sock );
mDNSlocal OSStatus UDPBeginRecv( UDPSocket * socket );
mDNSlocal void CALLBACK UDPEndRecv( DWORD err, DWORD bytesTransferred, LPWSAOVERLAPPED overlapped, DWORD flags );
+mDNSlocal void UDPCloseSocket( UDPSocket * sock );
mDNSlocal void CALLBACK UDPFreeSocket( UDPSocket * sock );
mDNSlocal mStatus SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa);
mDNSlocal void GetDDNSFQDN( domainname *const fqdn );
mDNSlocal void CheckFileShares( mDNS * const inMDNS );
mDNSlocal void SMBCallback(mDNS *const m, ServiceRecordSet *const srs, mStatus result);
mDNSlocal mDNSu8 IsWOMPEnabledForAdapter( const char * adapterName );
-mDNSlocal void FreeSocketEventsForSocket( mDNS * const inMDNS, void * sock );
-mDNSlocal void FreeSocketEvents( mDNS * const inMDNS );
-mDNSlocal void TCPSocketEventHandler( mDNS * const inMDNS, void * v );
-mDNSlocal void UDPSocketEventHandler( mDNS * const inMDNS, void * v );
+mDNSlocal void DispatchUDPEvent( mDNS * const m, UDPSocket * sock );
+mDNSlocal void DispatchTCPEvent( mDNS * const m, TCPSocket * sock );
#ifdef __cplusplus
}
//===========================================================================================================================
mDNSlocal mDNS_PlatformSupport gMDNSPlatformSupport;
-mDNSs32 mDNSPlatformOneSecond = 0;
-mDNSlocal UDPSocket * gUDPSocketList = NULL;
-mDNSlocal int gUDPSockets = 0;
-mDNSlocal BOOL gSocketEventsEnabled = FALSE;
-mDNSlocal GenLinkedList gSocketEvents;
+mDNSs32 mDNSPlatformOneSecond = 0;
+mDNSlocal UDPSocket * gUDPSockets = NULL;
+mDNSlocal int gUDPNumSockets = 0;
+mDNSlocal GenLinkedList gUDPDispatchableSockets;
+mDNSlocal GenLinkedList gTCPDispatchableSockets;
#if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS )
require_action( inMDNS->p->checkFileSharesTimer, exit, err = mStatus_UnknownErr );
inMDNS->p->checkFileSharesTimeout = 10; // Retry time for CheckFileShares() in seconds
mDNSPlatformOneSecond = 1000; // Use milliseconds as the quantum of time
- InitLinkedList( &gSocketEvents, offsetof( SocketEvent, next));
+ InitLinkedList( &gTCPDispatchableSockets, offsetof( TCPSocket, nextDispatchable ) );
+ InitLinkedList( &gUDPDispatchableSockets, offsetof( UDPSocket, nextDispatchable ) );
// Startup WinSock 2.2 or later.
inMDNS->p->unicastSock4.fd = INVALID_SOCKET;
inMDNS->p->unicastSock4.recvMsgPtr = NULL;
inMDNS->p->unicastSock4.ifd = NULL;
+ inMDNS->p->unicastSock4.overlapped.pending = FALSE;
inMDNS->p->unicastSock4.next = NULL;
inMDNS->p->unicastSock4.m = inMDNS;
inMDNS->p->unicastSock6.fd = INVALID_SOCKET;
inMDNS->p->unicastSock6.recvMsgPtr = NULL;
inMDNS->p->unicastSock6.ifd = NULL;
+ inMDNS->p->unicastSock6.overlapped.pending = FALSE;
inMDNS->p->unicastSock6.next = NULL;
inMDNS->p->unicastSock6.m = inMDNS;
#if ( MDNS_WINDOWS_ENABLE_IPV4 )
- if ( inMDNS->p->unicastSock4.fd != INVALID_SOCKET )
- {
- closesocket( inMDNS->p->unicastSock4.fd );
- inMDNS->p->unicastSock4.fd = INVALID_SOCKET;
- }
+ UDPCloseSocket( &inMDNS->p->unicastSock4 );
#endif
#if ( MDNS_WINDOWS_ENABLE_IPV6 )
- if ( inMDNS->p->unicastSock6.fd != INVALID_SOCKET )
- {
- closesocket( inMDNS->p->unicastSock6.fd );
- inMDNS->p->unicastSock6.fd = INVALID_SOCKET;
- }
+ UDPCloseSocket( &inMDNS->p->unicastSock6 );
#endif
// Clear out the APC queue
- SetSocketEventsEnabled( inMDNS, TRUE );
- while ( SleepEx( 0, TRUE ) == WAIT_IO_COMPLETION );
- SetSocketEventsEnabled( inMDNS, FALSE );
+ while ( SleepEx( 0, TRUE ) == WAIT_IO_COMPLETION )
+ {
+ DispatchSocketEvents( inMDNS );
+ }
WSACleanup();
// mDNSPlatformInterfaceIndexfromInterfaceID
//===========================================================================================================================
-mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID( mDNS * const inMDNS, mDNSInterfaceID inID )
+mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSBool suppressNetworkChange )
{
mDNSu32 index;
+ (void) suppressNetworkChange; // Unused
index = 0;
if( inID == mDNSInterface_LocalOnly )
)
{
struct sockaddr_in saddr;
- ( void ) hostname;
mStatus err = mStatus_NoError;
DEBUG_UNUSED( inInterfaceID );
+ ( void ) hostname;
if ( inDstIP->type != mDNSAddrType_IPv4 )
{
if ( sock->fd != INVALID_SOCKET )
{
- closesocket( sock->fd );
- sock->fd = INVALID_SOCKET;
+ TCPCloseSocket( sock );
QueueUserAPC( ( PAPCFUNC ) TCPFreeSocket, sock->m->p->mainThread, ( ULONG_PTR ) sock );
}
-
- FreeSocketEventsForSocket( sock->m, sock );
}
+
//===========================================================================================================================
// mDNSPlatformReadTCP
//===========================================================================================================================
memcpy( inBuffer, sock->bptr, bytesToCopy );
sock->bptr += bytesToCopy;
- ret = bytesToCopy;
-
- if ( bytesLeft == bytesToCopy )
+ if ( !sock->overlapped.pending && ( sock->bptr == sock->eptr ) )
{
- sock->lastError = TCPBeginRecv( sock );
-
- // If we can't immediately queue up another read, abort the connection
- // now, even if we successfully wrote bytes to the buffer.
- // We don't expect this to happen unless something is seriously borked.
- // If we run into this in the real world, we should consider queuing up
- // a user APC function to defer an explicit callback to the read event handler
- // to inform the consumer of the problem.
-
- if ( sock->lastError )
- {
- dlog( kDebugLevelError, DEBUG_NAME "TCPBeginRecv failed with error %d\n", sock->lastError );
- wsaError = sock->lastError;
- ret = -1;
- }
+ sock->bptr = sock->bbuf;
+ sock->eptr = sock->bbuf;
}
+
+ ret = bytesToCopy;
}
else
{
( void ) inMDNS;
+ sock->bptr = sock->bbuf;
+ sock->eptr = sock->bbuf;
+ sock->ebuf = sock->bbuf + sizeof( sock->bbuf );
+
dlog( kDebugLevelChatty, DEBUG_NAME "adding TCPSocket 0x%x:%d\n", sock, sock->fd );
err = TCPBeginRecv( sock );
require_noerr( err, exit );
DWORD flags = 0;
mStatus err;
- ZeroMemory( &sock->overlapped, sizeof( sock->overlapped ) );
- sock->overlapped.hEvent = sock;
+ dlog( kDebugLevelChatty, DEBUG_NAME "%s: sock = %d\n", __ROUTINE__, sock->fd );
+
+ check( !sock->overlapped.pending );
- sock->wbuf.buf = ( char* ) sock->buf;
- sock->wbuf.len = sizeof( sock->buf );
+ ZeroMemory( &sock->overlapped.data, sizeof( sock->overlapped.data ) );
+ sock->overlapped.data.hEvent = sock;
- err = WSARecv( sock->fd, &sock->wbuf, 1, &bytesReceived, &flags, &sock->overlapped, ( LPWSAOVERLAPPED_COMPLETION_ROUTINE ) TCPEndRecv );
+ sock->overlapped.wbuf.buf = ( char* ) sock->eptr;
+ sock->overlapped.wbuf.len = ( ULONG) ( sock->ebuf - sock->eptr );
+
+ err = WSARecv( sock->fd, &sock->overlapped.wbuf, 1, &bytesReceived, &flags, &sock->overlapped.data, ( LPWSAOVERLAPPED_COMPLETION_ROUTINE ) TCPEndRecv );
err = translate_errno( ( err == 0 ) || ( WSAGetLastError() == WSA_IO_PENDING ), WSAGetLastError(), kUnknownErr );
require_noerr( err, exit );
+ sock->overlapped.pending = TRUE;
+
exit:
return err;
( void ) flags;
+ dlog( kDebugLevelChatty, DEBUG_NAME "%s: error = %d, bytesTransferred = %d\n", __ROUTINE__, error, bytesTransferred );
sock = ( overlapped != NULL ) ? overlapped->hEvent : NULL;
+ require_action( sock, exit, error = ( DWORD ) mStatus_BadStateErr );
+ dlog( kDebugLevelChatty, DEBUG_NAME "%s: sock = %d\n", __ROUTINE__, sock->fd );
+ sock->overlapped.error = error;
+ sock->overlapped.bytesTransferred = bytesTransferred;
+ check( sock->overlapped.pending );
+ sock->overlapped.pending = FALSE;
- if ( sock && ( sock->fd != INVALID_SOCKET ) )
- {
- // <rdar://problem/7532492> mDNSResponder gets locking errors on Windows
- //
- // There seems to be a bug in WinSock with respect to Alertable I/O. According
- // to MSDN <http://msdn.microsoft.com/en-us/library/aa363772(VS.85).aspx>, Alertable I/O
- // callbacks will only be invoked during the following calls (when the caller sets
- // the appropriate flag):
- //
- // - SleepEx
- // - WaitForSingleObjectEx
- // - WaitForMultipleObjectsEx
- // - SignalObjectAndWait
- // - MsgWaitForMultipleObjectsEx
- //
- // However, we have seen callbacks be invoked during calls to bind() (and maybe others).
- // To workaround this, we set the gSocketEventsEnabled flag to be TRUE only when it's okay
- // to directly call the readEventHandler. Otherwise we queue the packet up and
- // dispatch it later.
-
- if ( gSocketEventsEnabled && !gSocketEvents.Head )
- {
- sock->lastError = error;
-
- if ( !error )
- {
- if ( bytesTransferred )
- {
- sock->bptr = sock->buf;
- sock->eptr = sock->buf + bytesTransferred;
- }
- else
- {
- sock->closed = TRUE;
- }
- }
+ // Queue this socket
- if ( sock->readEventHandler != NULL )
- {
- // Turn off socket events before calling the readEventHandler.
- // This will prevent reentrancy problems.
- //
- // Don't forget to reenable socket events afterwards.
-
- SetSocketEventsEnabled( sock->m, FALSE );
- sock->readEventHandler( sock );
- SetSocketEventsEnabled( sock->m, TRUE );
- }
- }
- else
- {
- TCPSocketEvent * event;
-
- event = malloc( sizeof( TCPSocketEvent ) );
- require_action( event, exit, error = ( DWORD ) kNoMemoryErr );
- event->super.sock = sock;
- event->super.handler = &TCPSocketEventHandler;
- event->super.next = NULL;
- event->error = error;
- event->bytesTransferred = bytesTransferred;
-
- if ( bytesTransferred )
- {
- memcpy( event->buf, sock->buf, bytesTransferred );
- }
-
- AddToTail( &gSocketEvents, event );
- }
- }
+ AddToTail( &gTCPDispatchableSockets, sock );
exit:
return;
}
+
//===========================================================================================================================
// mDNSPlatformUDPSocket
// Create the socket
- sock->fd = INVALID_SOCKET;
- sock->recvMsgPtr = m->p->unicastSock4.recvMsgPtr;
- sock->addr = m->p->unicastSock4.addr;
- sock->ifd = NULL;
- sock->m = m;
+ sock->fd = INVALID_SOCKET;
+ sock->recvMsgPtr = m->p->unicastSock4.recvMsgPtr;
+ sock->addr = m->p->unicastSock4.addr;
+ sock->ifd = NULL;
+ sock->overlapped.pending = FALSE;
+ sock->m = m;
// Try at most 10000 times to get a unique random port
// Bookkeeping
- sock->next = gUDPSocketList;
- gUDPSocketList = sock;
- gUDPSockets++;
+ sock->next = gUDPSockets;
+ gUDPSockets = sock;
+ gUDPNumSockets++;
exit:
mDNSexport void mDNSPlatformUDPClose( UDPSocket *sock )
{
- UDPSocket * current = gUDPSocketList;
+ UDPSocket * current = gUDPSockets;
UDPSocket * last = NULL;
while ( current )
{
if ( last == NULL )
{
- gUDPSocketList = sock->next;
+ gUDPSockets = sock->next;
}
else
{
// UDPEndRecv will check if the socket is valid, and if not, it will ignore
// the packet
- closesocket( sock->fd );
- sock->fd = INVALID_SOCKET;
+ UDPCloseSocket( sock );
QueueUserAPC( ( PAPCFUNC ) UDPFreeSocket, sock->m->p->mainThread, ( ULONG_PTR ) sock );
- FreeSocketEventsForSocket( sock->m, sock );
- gUDPSockets--;
+ gUDPNumSockets--;
break;
}
// mDNSPlatformSendRawPacket
//===========================================================================================================================
+mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason)\r
+ {\r
+ DEBUG_UNUSED( m );\r
+ DEBUG_UNUSED( allowSleep );\r
+ DEBUG_UNUSED( reason );\r
+ }\r
+
mDNSexport void mDNSPlatformSendRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID)
{
DEBUG_UNUSED( msg );
mDNSexport void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSAddr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID)
{
+ DEBUG_UNUSED( m );
DEBUG_UNUSED( tpa );
DEBUG_UNUSED( tha );
DEBUG_UNUSED( InterfaceID );
}
+mDNSexport void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result)
+ {
+ (void)m; // unused
+ (void)rr;
+ (void)result;
+ }
+
+
+
//===========================================================================================================================
// SetDomainSecrets
//===========================================================================================================================
while ( tok )
{
if ( ( strcmp( tok, "" ) != 0 ) && ( strcmp( tok, "." ) != 0 ) )
- mDNS_AddSearchDomain_CString(tok);
+ mDNS_AddSearchDomain_CString(tok, mDNSNULL);
tok = strtok( NULL, "," );
}
addr.ip.v4.b[2] & netmask.ip.v4.b[2],
addr.ip.v4.b[1] & netmask.ip.v4.b[1],
addr.ip.v4.b[0] & netmask.ip.v4.b[0]);
- mDNS_AddSearchDomain_CString(buffer);
+ mDNS_AddSearchDomain_CString(buffer, mDNSNULL);
}
}
{
mDNSAddr addr;
err = StringToAddress( &addr, ipAddr->IpAddress.String );
- if ( !err ) mDNS_AddDNSServer(m, mDNSNULL, mDNSInterface_Any, &addr, UnicastDNSPort);
+ if ( !err ) mDNS_AddDNSServer(m, mDNSNULL, mDNSInterface_Any, &addr, UnicastDNSPort, mDNSfalse, 0);
}
exit:
check_noerr( err );
}
- if ( domain && domain[0] ) mDNS_AddSearchDomain_CString(domain);
+ if ( domain && domain[0] ) mDNS_AddSearchDomain_CString(domain, mDNSNULL);
break;
}
(void) iteration;
}
+mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf)
+ {
+ (void) rr;
+ (void) intf;
+
+ return 1;
+ }
+
+
#if 0
#pragma mark -
#endif
check( inMDNS );
check( inMDNS->p );
- // Free any pending events received.
-
- FreeSocketEvents( inMDNS );
-
// Free any interfaces that were previously marked inactive and are no longer referenced by the mDNS cache.
// Interfaces are marked inactive, but not deleted immediately if they were still referenced by the mDNS cache
// so that remove events that occur after an interface goes away can still report the correct interface.
ifd = (mDNSInterfaceData *) calloc( 1, sizeof( *ifd ) );
require_action( ifd, exit, err = mStatus_NoMemoryErr );
- ifd->sock.fd = kInvalidSocketRef;
- ifd->sock.ifd = ifd;
- ifd->sock.next = NULL;
- ifd->sock.m = inMDNS;
- ifd->index = inIFA->ifa_extra.index;
- ifd->scopeID = inIFA->ifa_extra.index;
+ ifd->sock.fd = kInvalidSocketRef;
+ ifd->sock.overlapped.pending = FALSE;
+ ifd->sock.ifd = ifd;
+ ifd->sock.next = NULL;
+ ifd->sock.m = inMDNS;
+ ifd->index = inIFA->ifa_extra.index;
+ ifd->scopeID = inIFA->ifa_extra.index;
check( strlen( inIFA->ifa_name ) < sizeof( ifd->name ) );
strncpy( ifd->name, inIFA->ifa_name, sizeof( ifd->name ) - 1 );
ifd->name[ sizeof( ifd->name ) - 1 ] = '\0';
// Tear down the multicast socket.
- if ( inIFD->sock.fd != INVALID_SOCKET )
- {
- closesocket( inIFD->sock.fd );
- inIFD->sock.fd = INVALID_SOCKET;
- }
-
+ UDPCloseSocket( &inIFD->sock );
+
// If the interface is still referenced by items in the mDNS cache then put it on the inactive list. This keeps
// the InterfaceID valid so remove events report the correct interface. If it is no longer referenced, free it.
mDNSlocal OSStatus UDPBeginRecv( UDPSocket * sock )
{
- DWORD size;
- DWORD numTries;
- OSStatus err;
-
- require_action( socket != NULL, exit, err = kUnknownErr );
+ DWORD size;
+ DWORD numTries;
+ mStatus err;
+ dlog( kDebugLevelChatty, DEBUG_NAME "%s: sock = %d\n", __ROUTINE__, sock->fd );
+
+ require_action( sock != NULL, exit, err = mStatus_BadStateErr );
+ check( !sock->overlapped.pending );
+
// Initialize the buffer structure
- sock->wbuf.buf = (char *) &sock->packet;
- sock->wbuf.len = (u_long) sizeof( sock->packet );
- sock->srcAddrLen = sizeof( sock->srcAddr );
+ sock->overlapped.wbuf.buf = (char *) &sock->packet;
+ sock->overlapped.wbuf.len = (u_long) sizeof( sock->packet );
+ sock->srcAddrLen = sizeof( sock->srcAddr );
// Initialize the overlapped structure
- ZeroMemory( &sock->overlapped, sizeof( OVERLAPPED ) );
- sock->overlapped.hEvent = sock;
+ ZeroMemory( &sock->overlapped.data, sizeof( OVERLAPPED ) );
+ sock->overlapped.data.hEvent = sock;
numTries = 0;
{
sock->wmsg.name = ( LPSOCKADDR ) &sock->srcAddr;
sock->wmsg.namelen = sock->srcAddrLen;
- sock->wmsg.lpBuffers = &sock->wbuf;
+ sock->wmsg.lpBuffers = &sock->overlapped.wbuf;
sock->wmsg.dwBufferCount = 1;
sock->wmsg.Control.buf = ( CHAR* ) sock->controlBuffer;
sock->wmsg.Control.len = sizeof( sock->controlBuffer );
sock->wmsg.dwFlags = 0;
- err = sock->recvMsgPtr( sock->fd, &sock->wmsg, &size, &sock->overlapped, ( LPWSAOVERLAPPED_COMPLETION_ROUTINE ) UDPEndRecv );
+ err = sock->recvMsgPtr( sock->fd, &sock->wmsg, &size, &sock->overlapped.data, ( LPWSAOVERLAPPED_COMPLETION_ROUTINE ) UDPEndRecv );
err = translate_errno( ( err == 0 ) || ( WSAGetLastError() == WSA_IO_PENDING ), (OSStatus) WSAGetLastError(), kUnknownErr );
// <rdar://problem/7824093> iTunes 9.1 fails to install with Bonjour service on Windows 7 Ultimate
{
DWORD flags = 0;
- err = WSARecvFrom( sock->fd, &sock->wbuf, 1, NULL, &flags, ( LPSOCKADDR ) &sock->srcAddr, &sock->srcAddrLen, &sock->overlapped, ( LPWSAOVERLAPPED_COMPLETION_ROUTINE ) UDPEndRecv );
+ err = WSARecvFrom( sock->fd, &sock->overlapped.wbuf, 1, NULL, &flags, ( LPSOCKADDR ) &sock->srcAddr, &sock->srcAddrLen, &sock->overlapped.data, ( LPWSAOVERLAPPED_COMPLETION_ROUTINE ) UDPEndRecv );
err = translate_errno( ( err == 0 ) || ( WSAGetLastError() == WSA_IO_PENDING ), ( OSStatus ) WSAGetLastError(), kUnknownErr );
}
}
while ( ( ( err == WSAECONNRESET ) || ( err == WSAEFAULT ) ) && ( numTries++ < 100 ) );
+ sock->overlapped.pending = TRUE;
+
exit:
if ( err )
UDPSocket * sock = NULL;
( void ) flags;
-
+
+ dlog( kDebugLevelChatty, DEBUG_NAME "%s: err = %d, bytesTransferred = %d\n", __ROUTINE__, err, bytesTransferred );
require_action_quiet( err != WSA_OPERATION_ABORTED, exit, err = ( DWORD ) kUnknownErr );
require_noerr( err, exit );
sock = ( overlapped != NULL ) ? overlapped->hEvent : NULL;
require_action( sock != NULL, exit, err = ( DWORD ) kUnknownErr );
-
- // If we've closed the socket, then we want to ignore
- // this read. The packet might have been queued before
- // the socket was closed.
-
- if ( sock->fd != INVALID_SOCKET )
- {
- const mDNSInterfaceID iid = sock->ifd ? sock->ifd->interfaceInfo.InterfaceID : NULL;
- mDNSAddr srcAddr;
- mDNSIPPort srcPort;
- mDNSAddr dstAddr;
- mDNSIPPort dstPort;
- mDNSu8 * end;
+ dlog( kDebugLevelChatty, DEBUG_NAME "%s: sock = %d\n", __ROUTINE__, sock->fd );
+ sock->overlapped.error = err;
+ sock->overlapped.bytesTransferred = bytesTransferred;
+ check( sock->overlapped.pending );
+ sock->overlapped.pending = FALSE;
- // Translate the source of this packet into mDNS data types
+ // Translate the source of this packet into mDNS data types
- SockAddrToMDNSAddr( (struct sockaddr *) &sock->srcAddr, &srcAddr, &srcPort );
+ SockAddrToMDNSAddr( (struct sockaddr *) &sock->srcAddr, &sock->overlapped.srcAddr, &sock->overlapped.srcPort );
- // Initialize the destination of this packet. Just in case
- // we can't determine this info because we couldn't call
- // WSARecvMsg (recvMsgPtr)
+ // Initialize the destination of this packet. Just in case
+ // we can't determine this info because we couldn't call
+ // WSARecvMsg (recvMsgPtr)
- dstAddr = sock->addr;
- dstPort = sock->port;
+ sock->overlapped.dstAddr = sock->addr;
+ sock->overlapped.dstPort = sock->port;
- if ( sock->recvMsgPtr )
- {
- LPWSACMSGHDR header;
- LPWSACMSGHDR last = NULL;
- int count = 0;
+ if ( sock->recvMsgPtr )
+ {
+ LPWSACMSGHDR header;
+ LPWSACMSGHDR last = NULL;
+ int count = 0;
- // Parse the control information. Reject packets received on the wrong interface.
+ // Parse the control information. Reject packets received on the wrong interface.
- // <rdar://problem/7832196> INSTALL: Bonjour 2.0 on Windows can not start / stop
- //
- // There seems to be an interaction between Bullguard and this next bit of code.
- // When a user's machine is running Bullguard, the control information that is
- // returned is corrupted, and the code would go into an infinite loop. We'll add
- // two bits of defensive coding here. The first will check that each pointer to
- // the LPWSACMSGHDR that is returned in the for loop is different than the last.
- // This fixes the problem with Bullguard. The second will break out of this loop
- // after 100 iterations, just in case the corruption isn't caught by the first
- // check.
-
- for( header = WSA_CMSG_FIRSTHDR( &sock->wmsg ); header; header = WSA_CMSG_NXTHDR( &sock->wmsg, header ) )
+ // <rdar://problem/7832196> INSTALL: Bonjour 2.0 on Windows can not start / stop
+ //
+ // There seems to be an interaction between Bullguard and this next bit of code.
+ // When a user's machine is running Bullguard, the control information that is
+ // returned is corrupted, and the code would go into an infinite loop. We'll add
+ // two bits of defensive coding here. The first will check that each pointer to
+ // the LPWSACMSGHDR that is returned in the for loop is different than the last.
+ // This fixes the problem with Bullguard. The second will break out of this loop
+ // after 100 iterations, just in case the corruption isn't caught by the first
+ // check.
+
+ for ( header = WSA_CMSG_FIRSTHDR( &sock->wmsg ); header; header = WSA_CMSG_NXTHDR( &sock->wmsg, header ) )
+ {
+ if ( ( header != last ) && ( ++count < 100 ) )
{
- if ( ( header != last ) && ( ++count < 100 ) )
- {
- last = header;
+ last = header;
- if( ( header->cmsg_level == IPPROTO_IP ) && ( header->cmsg_type == IP_PKTINFO ) )
- {
- IN_PKTINFO * ipv4PacketInfo;
+ if ( ( header->cmsg_level == IPPROTO_IP ) && ( header->cmsg_type == IP_PKTINFO ) )
+ {
+ IN_PKTINFO * ipv4PacketInfo;
- ipv4PacketInfo = (IN_PKTINFO *) WSA_CMSG_DATA( header );
-
- if ( sock->ifd != NULL )
- {
- require_action( ipv4PacketInfo->ipi_ifindex == sock->ifd->index, exit, err = ( DWORD ) kMismatchErr );
- }
+ ipv4PacketInfo = (IN_PKTINFO *) WSA_CMSG_DATA( header );
- dstAddr.type = mDNSAddrType_IPv4;
- dstAddr.ip.v4.NotAnInteger = ipv4PacketInfo->ipi_addr.s_addr;
- }
- else if( ( header->cmsg_level == IPPROTO_IPV6 ) && ( header->cmsg_type == IPV6_PKTINFO ) )
+ if ( sock->ifd != NULL )
{
- IN6_PKTINFO * ipv6PacketInfo;
-
- ipv6PacketInfo = (IN6_PKTINFO *) WSA_CMSG_DATA( header );
-
- if ( sock->ifd != NULL )
- {
- require_action( ipv6PacketInfo->ipi6_ifindex == ( sock->ifd->index - kIPv6IfIndexBase ), exit, err = ( DWORD ) kMismatchErr );
- }
-
- dstAddr.type = mDNSAddrType_IPv6;
- dstAddr.ip.v6 = *( (mDNSv6Addr *) &ipv6PacketInfo->ipi6_addr );
+ require_action( ipv4PacketInfo->ipi_ifindex == sock->ifd->index, exit, err = ( DWORD ) kMismatchErr );
}
+
+ sock->overlapped.dstAddr.type = mDNSAddrType_IPv4;
+ sock->overlapped.dstAddr.ip.v4.NotAnInteger = ipv4PacketInfo->ipi_addr.s_addr;
}
- else
+ else if( ( header->cmsg_level == IPPROTO_IPV6 ) && ( header->cmsg_type == IPV6_PKTINFO ) )
{
- static BOOL loggedMessage = FALSE;
-
- if ( !loggedMessage )
+ IN6_PKTINFO * ipv6PacketInfo;
+
+ ipv6PacketInfo = (IN6_PKTINFO *) WSA_CMSG_DATA( header );
+
+ if ( sock->ifd != NULL )
{
- LogMsg( "UDPEndRecv: WSARecvMsg control information error." );
- loggedMessage = TRUE;
+ require_action( ipv6PacketInfo->ipi6_ifindex == ( sock->ifd->index - kIPv6IfIndexBase ), exit, err = ( DWORD ) kMismatchErr );
}
- break;
+ sock->overlapped.dstAddr.type = mDNSAddrType_IPv6;
+ sock->overlapped.dstAddr.ip.v6 = *( (mDNSv6Addr *) &ipv6PacketInfo->ipi6_addr );
}
}
- }
+ else
+ {
+ static BOOL loggedMessage = FALSE;
- // Dispatch the packet to mDNS.
-
- dlog( kDebugLevelChatty, DEBUG_NAME "packet received\n" );
- dlog( kDebugLevelChatty, DEBUG_NAME " size = %d\n", bytesTransferred );
- dlog( kDebugLevelChatty, DEBUG_NAME " src = %#a:%u\n", &srcAddr, ntohs( srcPort.NotAnInteger ) );
- dlog( kDebugLevelChatty, DEBUG_NAME " dst = %#a:%u\n", &dstAddr, ntohs( dstPort.NotAnInteger ) );
-
- if ( sock->ifd != NULL )
- {
- dlog( kDebugLevelChatty, DEBUG_NAME " interface = %#a (index=0x%08X)\n", &sock->ifd->interfaceInfo.ip, sock->ifd->index );
+ if ( !loggedMessage )
+ {
+ LogMsg( "UDPEndRecv: WSARecvMsg control information error." );
+ loggedMessage = TRUE;
+ }
+
+ break;
+ }
}
+ }
- dlog( kDebugLevelChatty, DEBUG_NAME "\n" );
+ dlog( kDebugLevelChatty, DEBUG_NAME "packet received\n" );
+ dlog( kDebugLevelChatty, DEBUG_NAME " size = %d\n", bytesTransferred );
+ dlog( kDebugLevelChatty, DEBUG_NAME " src = %#a:%u\n", &sock->overlapped.srcAddr, ntohs( sock->overlapped.srcPort.NotAnInteger ) );
+ dlog( kDebugLevelChatty, DEBUG_NAME " dst = %#a:%u\n", &sock->overlapped.dstAddr, ntohs( sock->overlapped.dstPort.NotAnInteger ) );
- end = ( (mDNSu8 *) &sock->packet ) + bytesTransferred;
+ if ( sock->ifd != NULL )
+ {
+ dlog( kDebugLevelChatty, DEBUG_NAME " interface = %#a (index=0x%08X)\n", &sock->ifd->interfaceInfo.ip, sock->ifd->index );
+ }
- // <rdar://problem/7532492> mDNSResponder gets locking errors on Windows
- //
- // There seems to be a bug in WinSock with respect to Alertable I/O. According
- // to MSDN <http://msdn.microsoft.com/en-us/library/aa363772(VS.85).aspx>, Alertable I/O
- // callbacks will only be invoked during the following calls (when the caller sets
- // the appropriate flag):
- //
- // - SleepEx
- // - WaitForSingleObjectEx
- // - WaitForMultipleObjectsEx
- // - SignalObjectAndWait
- // - MsgWaitForMultipleObjectsEx
- //
- // However, we have seen callbacks be invoked during calls to bind() (and maybe others).
- // To workaround this, we set the gSocketEventsEnabled flag to be TRUE only when it's okay
- // to directly call mDNSCoreReceive. Otherwise we queue the packet up and dispatch it later.
+ dlog( kDebugLevelChatty, DEBUG_NAME "\n" );
- if ( gSocketEventsEnabled && !gSocketEvents.Head )
- {
- // Turn off socket events before calling mDNSCoreReceive().
- // This will prevent reentrancy problems.
- //
- // Don't forget to reenable socket events afterwards.
-
- SetSocketEventsEnabled( sock->m, FALSE );
- mDNSCoreReceive( sock->m, &sock->packet, end, &srcAddr, srcPort, &dstAddr, dstPort, iid );
- SetSocketEventsEnabled( sock->m, TRUE );
- }
- else
- {
- UDPSocketEvent * event;
-
- event = malloc( sizeof( UDPSocketEvent ) );
- require_action( event, exit, err = ( DWORD ) kNoMemoryErr );
- event->super.sock = sock;
- event->super.handler = &UDPSocketEventHandler;
- event->super.next = NULL;
- event->iid = iid;
- memcpy( &event->packet, &sock->packet, sizeof( event->packet ) );
- event->end = ( (mDNSu8 *) &event->packet ) + bytesTransferred;
- event->srcAddr = srcAddr;
- event->dstAddr = dstAddr;
- event->srcPort = srcPort;
- event->dstPort = dstPort;
-
- AddToTail( &gSocketEvents, event );
- }
- }
+ // Queue this socket
+
+ AddToTail( &gUDPDispatchableSockets, sock );
exit:
- // If the socket is still good, then start up another asynchronous read
-
- if ( sock && ( sock->fd != INVALID_SOCKET ) )
- {
- err = UDPBeginRecv( sock );
- check_noerr( err );
- }
+ return;
}
}
+//===========================================================================================================================
+// TCPCloseSocket
+//===========================================================================================================================
+
+mDNSlocal void
+TCPCloseSocket( TCPSocket * sock )
+{
+ dlog( kDebugLevelChatty, DEBUG_NAME "closing TCPSocket 0x%x:%d\n", sock, sock->fd );
+
+ RemoveFromList( &gTCPDispatchableSockets, sock );
+
+ if ( sock->fd != INVALID_SOCKET )
+ {
+ closesocket( sock->fd );
+ sock->fd = INVALID_SOCKET;
+ }
+}
+
+
//===========================================================================================================================
// TCPFreeSocket
//===========================================================================================================================
}
+//===========================================================================================================================
+// UDPCloseSocket
+//===========================================================================================================================
+
+mDNSlocal void
+UDPCloseSocket( UDPSocket * sock )
+{
+ dlog( kDebugLevelChatty, DEBUG_NAME "closing UDPSocket %d\n", sock->fd );
+
+ RemoveFromList( &gUDPDispatchableSockets, sock );
+
+ if ( sock->fd != INVALID_SOCKET )
+ {
+ closesocket( sock->fd );
+ sock->fd = INVALID_SOCKET;
+ }
+}
+
+
//===========================================================================================================================
// UDPFreeSocket
//===========================================================================================================================
{
check( sock );
- dlog( kDebugLevelChatty, DEBUG_NAME "freeing UDPSocket %d (%##a)\n", sock->fd, &sock->addr );
+ dlog( kDebugLevelChatty, DEBUG_NAME "freeing UDPSocket %d\n", sock->fd );
if ( sock->fd != INVALID_SOCKET )
{
require_action( ptr, exit, err = mStatus_NoMemoryErr );
}
- err = mDNS_SetSecretForDomain(m, ptr, &domain, &key, outSecret, mDNSfalse );
+ err = mDNS_SetSecretForDomain(m, ptr, &domain, &key, outSecret, NULL );
require_action( err != mStatus_BadParamErr, exit, if (!foundInList ) mDNSPlatformMemFree( ptr ) );
debugf("Setting shared secret for zone %s with key %##s", outDomain, key.c);
}
-void
-SetSocketEventsEnabled( mDNS * const inMDNS, BOOL enabled )
+void
+DispatchSocketEvents( mDNS * const inMDNS )
{
- DEBUG_UNUSED( inMDNS );
+ UDPSocket * udpSock;
+ TCPSocket * tcpSock;
- if ( enabled )
+ while ( ( udpSock = ( UDPSocket* ) gUDPDispatchableSockets.Head ) != NULL )
{
- int numEvents = 0;
-
- check( !gSocketEventsEnabled );
-
- // If we're enabled, then drain the queue right now.
-
- while ( gSocketEvents.Head && ( numEvents < 100 ) )
- {
- SocketEvent * event = ( SocketEvent* ) gSocketEvents.Head;
- RemoveFromList( &gSocketEvents, event );
- check( event->handler );
-
- // At this point we're going to call our event handler, and
- // gSocketEventsEnabled should be FALSE. So if the callback
- // does anything that causes alertable I/O callbacks to be
- // invoked, we'll queue the packets instead of invoking core
- // callbacks reentrantly.
- //
- // One potential problem here is a pathological case of
- // continuing to queue packets during invocation of
- // the callback, and thus never exiting out of this loop.
- // This is entirely theoretical as we've never seen it happen,
- // but just to be overly cautious, we'll only process a maximum of
- // 100 events here just in case.
-
- event->handler( inMDNS, event );
- free( event );
-
- numEvents++;
- }
+ dlog( kDebugLevelChatty, DEBUG_NAME "%s: calling DispatchUDPEvent on socket %d, error = %d, bytesTransferred = %d\n",
+ __ROUTINE__, udpSock->fd, udpSock->overlapped.error, udpSock->overlapped.bytesTransferred );
+ RemoveFromList( &gUDPDispatchableSockets, udpSock );
+ DispatchUDPEvent( inMDNS, udpSock );
+ }
+
+ while ( ( tcpSock = ( TCPSocket* ) gTCPDispatchableSockets.Head ) != NULL )
+ {
+ dlog( kDebugLevelChatty, DEBUG_NAME "%s: calling DispatchTCPEvent on socket %d, error = %d, bytesTransferred = %d\n",
+ __ROUTINE__, tcpSock->fd, tcpSock->overlapped.error, tcpSock->overlapped.bytesTransferred );
+ RemoveFromList( &gTCPDispatchableSockets, tcpSock );
+ DispatchTCPEvent( inMDNS, tcpSock );
}
-
- gSocketEventsEnabled = enabled;
}
mDNSlocal void
-FreeSocketEventsForSocket( mDNS * const inMDNS, void * sock )
+DispatchUDPEvent( mDNS * const inMDNS, UDPSocket * sock )
{
- SocketEvent * event;
+ ( void ) inMDNS;
- DEBUG_UNUSED( inMDNS );
+ // If we've closed the socket, then we want to ignore
+ // this read. The packet might have been queued before
+ // the socket was closed.
- for ( event = ( SocketEvent* ) gSocketEvents.Head; event; event = event->next )
+ if ( sock->fd != INVALID_SOCKET )
{
- if ( event->sock == sock )
- {
- RemoveFromList( &gSocketEvents, event );
- free( event );
- break;
- }
- }
-}
+ const mDNSInterfaceID iid = sock->ifd ? sock->ifd->interfaceInfo.InterfaceID : NULL;
+ mDNSu8 * end = ( (mDNSu8 *) &sock->packet ) + sock->overlapped.bytesTransferred;
+ dlog( kDebugLevelChatty, DEBUG_NAME "calling mDNSCoreReceive on socket: %d\n", sock->fd );
+ mDNSCoreReceive( sock->m, &sock->packet, end, &sock->overlapped.srcAddr, sock->overlapped.srcPort, &sock->overlapped.dstAddr, sock->overlapped.dstPort, iid );
+ }
-mDNSlocal void
-FreeSocketEvents( mDNS * const inMDNS )
-{
- DEBUG_UNUSED( inMDNS );
+ // If the socket is still good, then start up another asynchronous read
- while ( gSocketEvents.Head )
+ if ( sock->fd != INVALID_SOCKET )
{
- SocketEvent * event = ( SocketEvent* ) gSocketEvents.Head;
- RemoveFromList( &gSocketEvents, event );
- free( event );
+ int err = UDPBeginRecv( sock );
+ check_noerr( err );
}
}
mDNSlocal void
-TCPSocketEventHandler( mDNS * const inMDNS, void * v )
+DispatchTCPEvent( mDNS * const inMDNS, TCPSocket * sock )
{
- TCPSocketEvent * event = ( TCPSocketEvent* ) v;
- TCPSocket * sock = ( TCPSocket* ) event->super.sock;
-
- DEBUG_UNUSED( inMDNS );
- check( sock );
-
- sock->lastError = event->error;
+ ( void ) inMDNS;
- if ( !event->error )
+ if ( sock->fd != INVALID_SOCKET )
{
- if ( event->bytesTransferred )
+ sock->eptr += sock->overlapped.bytesTransferred;
+ sock->lastError = sock->overlapped.error;
+
+ if ( !sock->overlapped.error && !sock->overlapped.bytesTransferred )
{
- memcpy( sock->buf, event->buf, event->bytesTransferred );
- sock->bptr = sock->buf;
- sock->eptr = sock->buf + event->bytesTransferred;
+ sock->closed = TRUE;
}
- else
+
+ if ( sock->readEventHandler != NULL )
{
- sock->closed = TRUE;
+ dlog( kDebugLevelChatty, DEBUG_NAME "calling TCP read handler on socket: %d\n", sock->fd );
+ sock->readEventHandler( sock );
}
}
- if ( sock->readEventHandler != NULL )
+ // If the socket is still good, then start up another asynchronous read
+
+ if ( !sock->closed && ( sock->fd != INVALID_SOCKET ) )
{
- sock->readEventHandler( sock );
+ int err = TCPBeginRecv( sock );
+ check_noerr( err );
}
}
-
-
-mDNSlocal void
-UDPSocketEventHandler( mDNS * const inMDNS, void * v )
-{
- UDPSocketEvent * event = ( UDPSocketEvent* ) v;
-
- mDNSCoreReceive( inMDNS, &event->packet, event->end, &event->srcAddr, event->srcPort, &event->dstAddr, event->dstPort, event->iid );
-}
extern "C" {
#endif
+
+typedef struct Overlapped
+{
+ BOOL pending;
+ OVERLAPPED data;
+ WSABUF wbuf;
+ DWORD error;
+ DWORD bytesTransferred;
+ mDNSAddr srcAddr;
+ mDNSIPPort srcPort;
+ mDNSAddr dstAddr;
+ mDNSIPPort dstPort;
+} Overlapped;
+
+
typedef void ( *TCPReadEventHandler )( TCPSocket * sock );
typedef void ( *TCPUserCallback )();
BOOL connected;
TCPUserCallback userCallback;
void * userContext;
+ Overlapped overlapped;
DWORD lastError;
BOOL closed;\r
- OVERLAPPED overlapped;
- WSABUF wbuf;
- uint8_t buf[ 4192 ];
+ uint8_t bbuf[ 4192 ];
uint8_t * bptr;
uint8_t * eptr;
+ uint8_t * ebuf;
+ TCPSocket * nextDispatchable;
mDNS * m;
};
// dstAddr from WSARecvMsg we use this value instead.
SOCKET fd;
LPFN_WSARECVMSG recvMsgPtr;
- OVERLAPPED overlapped;
+ Overlapped overlapped;
WSAMSG wmsg;
- WSABUF wbuf;
DNSMessage packet;
uint8_t controlBuffer[ 128 ];
struct sockaddr_storage srcAddr; // This is filled in by the WSARecv* function
INT srcAddrLen; // See above
struct mDNSInterfaceData * ifd;
+ UDPSocket * nextDispatchable;
UDPSocket * next;
mDNS * m;
};
-typedef void ( *SocketEventHandler )( mDNS * const inMDNS, void * v );
-
-
-typedef struct SocketEvent
-{
- void * sock;
- SocketEventHandler handler;
- struct SocketEvent * next;
-} SocketEvent;
-
-
-typedef struct TCPSocketEvent
-{
- struct SocketEvent super;
- DWORD error;
- DWORD bytesTransferred;
- uint8_t buf[ 4192 ];
-} TCPSocketEvent;
-
-
-typedef struct UDPSocketEvent
-{
- struct SocketEvent super;
- mDNSInterfaceID iid;
- DNSMessage packet;
- mDNSu8 * end;
- mDNSAddr srcAddr;
- mDNSIPPort srcPort;
- mDNSAddr dstAddr;
- mDNSIPPort dstPort;
-} UDPSocketEvent;
-
-
-
//---------------------------------------------------------------------------------------------------------------------------
/*! @struct mDNSInterfaceData
extern mStatus SetupInterfaceList( mDNS * const inMDNS );
extern mStatus TearDownInterfaceList( mDNS * const inMDNS );
extern BOOL IsWOMPEnabled();
-extern void SetSocketEventsEnabled( mDNS * const inMDNS, BOOL val );
+extern void DispatchSocketEvents( mDNS * const inMDNS );
#ifdef __cplusplus