From 294beb6e262ed56f87e7501809b49879b5b24e76 Mon Sep 17 00:00:00 2001 From: Apple Date: Fri, 13 May 2011 22:51:13 +0000 Subject: [PATCH] mDNSResponder-320.5.tar.gz --- .../extension/content/bonjour4firefox.xul | 2 +- Clients/dns-sd.c | 49 +- .../mDNSNetMonitor.vcproj | 4 +- Makefile | 4 +- mDNSCore/DNSCommon.c | 151 +- mDNSCore/DNSCommon.h | 2 + mDNSCore/DNSDigest.c | 2 +- mDNSCore/mDNS.c | 1772 +++++++++++++---- mDNSCore/mDNSEmbeddedAPI.h | 198 +- mDNSCore/uDNS.c | 361 ++-- mDNSCore/uDNS.h | 8 +- mDNSMacOS9/SubTypeTester.c | 2 +- mDNSMacOSX/BonjourEvents-Info.plist | 49 + mDNSMacOSX/BonjourEvents.c | 1135 +++++++++++ mDNSMacOSX/P2PPacketFilter.c | 297 +++ mDNSMacOSX/P2PPacketFilter.h | 29 + mDNSMacOSX/daemon.c | 205 +- mDNSMacOSX/helper-stubs.c | 45 +- mDNSMacOSX/helper.c | 97 +- mDNSMacOSX/helper.h | 16 +- mDNSMacOSX/helpermsg.defs | 12 +- mDNSMacOSX/mDNSMacOSX.c | 1577 +++++++++++++-- mDNSMacOSX/mDNSMacOSX.h | 25 +- mDNSMacOSX/mDNSResponder.sb | 14 +- .../mDNSResponder.xcodeproj/project.pbxproj | 693 ++++++- mDNSPosix/Identify.c | 7 +- mDNSPosix/Makefile | 2 +- mDNSPosix/NetMonitor.c | 9 +- mDNSPosix/ProxyResponder.c | 10 +- mDNSPosix/Responder.c | 2 +- mDNSPosix/mDNSPosix.c | 33 +- mDNSResponder.sln | 30 +- mDNSShared/PlatformCommon.c | 2 +- mDNSShared/dns_sd.h | 30 +- mDNSShared/dnsextd.c | 14 +- mDNSShared/dnssd_clientshim.c | 21 +- mDNSShared/dnssd_clientstub.c | 61 +- mDNSShared/dnssd_ipc.h | 4 +- mDNSShared/uds_daemon.c | 1139 ++++++++--- mDNSShared/uds_daemon.h | 1 + mDNSWindows/DLL/dnssd.vcproj | 16 +- mDNSWindows/DLLStub/DLLStub.vcproj | 8 +- mDNSWindows/SystemService/Service.c | 22 +- mDNSWindows/SystemService/Service.vcproj | 8 +- mDNSWindows/mDNSWin32.c | 682 +++---- mDNSWindows/mDNSWin32.h | 62 +- 46 files changed, 7182 insertions(+), 1730 deletions(-) create mode 100644 mDNSMacOSX/BonjourEvents-Info.plist create mode 100644 mDNSMacOSX/BonjourEvents.c create mode 100644 mDNSMacOSX/P2PPacketFilter.c create mode 100644 mDNSMacOSX/P2PPacketFilter.h diff --git a/Clients/FirefoxExtension/extension/content/bonjour4firefox.xul b/Clients/FirefoxExtension/extension/content/bonjour4firefox.xul index 7fd0392..69e5efe 100755 --- a/Clients/FirefoxExtension/extension/content/bonjour4firefox.xul +++ b/Clients/FirefoxExtension/extension/content/bonjour4firefox.xul @@ -180,7 +180,7 @@ if ( treeBrowseList.currentIndex == 0 ) { - window._content.location="http://www.apple.com/bonjour"; + window._content.location="http://www.apple.com/macosx/features/bonjour"; } else { diff --git a/Clients/dns-sd.c b/Clients/dns-sd.c index 826245b..bf6cc10 100644 --- a/Clients/dns-sd.c +++ b/Clients/dns-sd.c @@ -736,7 +736,12 @@ static void DNSSD_API qr_reply(DNSServiceRef sdref, const DNSServiceFlags flags, 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"); @@ -947,7 +952,7 @@ static void getip(const char *const name, struct sockaddr_storage *result) 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. @@ -955,11 +960,12 @@ static DNSServiceErrorType RegisterProxyAddressRecord(DNSServiceRef sdref, const // 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); } @@ -971,9 +977,8 @@ static DNSServiceErrorType RegisterProxyAddressRecord(DNSServiceRef sdref, const #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] = ""; @@ -1008,7 +1013,7 @@ static DNSServiceErrorType RegisterService(DNSServiceRef *sdref, //flags |= kDNSServiceFlagsAllowRemoteQuery; //flags |= kDNSServiceFlagsNoAutoRename; - + return(DNSServiceRegister(sdref, flags, opinterface, nam, typ, dom, host, registerPort.NotAnInteger, (uint16_t) (ptr-txt), txt, reg_reply, NULL)); } @@ -1025,6 +1030,7 @@ int main(int argc, char **argv) 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 -- @@ -1063,6 +1069,14 @@ int main(int argc, char **argv) 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]); @@ -1073,7 +1087,7 @@ int main(int argc, char **argv) } 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 @@ -1104,7 +1118,7 @@ int main(int argc, char **argv) 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]; @@ -1136,26 +1150,25 @@ int main(int argc, char **argv) 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]); @@ -1191,8 +1204,8 @@ int main(int argc, char **argv) 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; } diff --git a/Clients/mDNSNetMonitor.VisualStudio/mDNSNetMonitor.vcproj b/Clients/mDNSNetMonitor.VisualStudio/mDNSNetMonitor.vcproj index 122889c..82e17e3 100755 --- a/Clients/mDNSNetMonitor.VisualStudio/mDNSNetMonitor.vcproj +++ b/Clients/mDNSNetMonitor.VisualStudio/mDNSNetMonitor.vcproj @@ -40,7 +40,7 @@ 0x7FFFFFFFUL / mDNSPlatformOneSecond) ttl = 0x7FFFFFFFUL / mDNSPlatformOneSecond; @@ -1033,6 +1056,7 @@ mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mD 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) @@ -1074,7 +1098,12 @@ mDNSexport void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID I 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; } @@ -1187,6 +1216,13 @@ mDNSexport mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBod 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); @@ -1206,6 +1242,14 @@ mDNSexport mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr 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); @@ -1214,10 +1258,6 @@ mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr 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. @@ -1227,8 +1267,91 @@ mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr 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); @@ -1747,7 +1870,7 @@ mDNSexport mDNSu8 *putZone(DNSMessage *const msg, mDNSu8 *ptr, mDNSu8 *limit, co 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; @@ -1817,7 +1940,7 @@ mDNSexport mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domain 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); @@ -1832,7 +1955,7 @@ mDNSexport mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease) 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); @@ -1851,7 +1974,7 @@ mDNSexport mDNSu8 *putHINFO(const mDNS *const m, DNSMessage *const msg, mDNSu8 * 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; @@ -2594,6 +2717,7 @@ mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m) } 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); @@ -2619,12 +2743,13 @@ mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m) 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"); @@ -2641,9 +2766,11 @@ mDNSexport void ShowTaskSchedulingError(mDNS *const m) 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"); diff --git a/mDNSCore/DNSCommon.h b/mDNSCore/DNSCommon.h index 401d571..08c636d 100644 --- a/mDNSCore/DNSCommon.h +++ b/mDNSCore/DNSCommon.h @@ -165,6 +165,7 @@ extern mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr, co 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); @@ -226,6 +227,7 @@ extern mDNSu8 *putHINFO(const mDNS *const m, DNSMessage *const msg, mDNSu8 *ptr, #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); diff --git a/mDNSCore/DNSDigest.c b/mDNSCore/DNSDigest.c index d3d0a5c..b4a0158 100644 --- a/mDNSCore/DNSDigest.c +++ b/mDNSCore/DNSDigest.c @@ -1345,7 +1345,7 @@ mDNSexport void DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, DomainAuthI 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); diff --git a/mDNSCore/mDNS.c b/mDNSCore/mDNS.c index 51cc367..005983c 100755 --- a/mDNSCore/mDNS.c +++ b/mDNSCore/mDNS.c @@ -75,6 +75,8 @@ void WCFConnectionDealloc(WCFConnection* c) __attribute__((weak_import)); 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); // *************************************************************************** @@ -110,6 +112,30 @@ mDNSexport const char *const mDNS_DomainTypeNames[] = #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) @@ -130,6 +156,169 @@ mDNSexport void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q) } } +mDNSlocal void ReleaseAuthEntity(AuthHash *r, AuthEntity *e) + { +#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING >= 1 + unsigned int i; + for (i=0; inext = 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; @@ -186,10 +375,83 @@ mDNSexport char *InterfaceNameForID(mDNS *const m, const mDNSInterfaceID Interfa 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)) { @@ -204,11 +466,50 @@ mDNSlocal void AnswerLocalQuestionWithLocalAuthRecord(mDNS *const m, DNSQuestion 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, @@ -229,26 +530,25 @@ mDNSlocal void AnswerAllLocalQuestionsWithLocalAuthRecord(mDNS *const m, AuthRec 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); + } // *************************************************************************** @@ -521,7 +821,7 @@ mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr) 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; @@ -648,6 +948,78 @@ mDNSexport void ActivateUnicastRegistration(mDNS *const m, AuthRecord *const rr) #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) { @@ -668,22 +1040,45 @@ 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); } @@ -697,7 +1092,7 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) 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); @@ -705,8 +1100,10 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) } } - // 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) @@ -829,31 +1226,24 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) 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)) { @@ -872,13 +1262,22 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const 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)); @@ -893,8 +1292,24 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const 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 @@ -947,8 +1362,25 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, 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) { @@ -971,8 +1403,16 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, 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; @@ -1000,7 +1440,7 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, 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)); } @@ -1093,10 +1533,18 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, } 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? @@ -1638,6 +2086,7 @@ mDNSlocal void SendResponses(mDNS *const m) 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; } } @@ -1785,7 +2234,16 @@ mDNSlocal void SendResponses(mDNS *const m) // 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; @@ -1892,7 +2350,7 @@ mDNSlocal void SendResponses(mDNS *const m) 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)); @@ -1929,7 +2387,7 @@ mDNSlocal void SendResponses(mDNS *const m) 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); @@ -1949,6 +2407,7 @@ mDNSlocal void SendResponses(mDNS *const m) 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); @@ -1981,7 +2440,7 @@ mDNSlocal void SendResponses(mDNS *const m) 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; } @@ -2332,6 +2791,7 @@ mDNSlocal void mDNSSendWakeOnResolve(mDNS *const m, DNSQuestion *q) } LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed WakeOnResolve name %##s", q->qname.c); } + mDNSlocal mDNSBool AccelerateThisQuery(mDNS *const m, DNSQuestion *q) { @@ -2701,7 +3161,7 @@ mDNSlocal void SendQueries(mDNS *const m) 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); @@ -2750,7 +3210,7 @@ mDNSlocal void SendQueries(mDNS *const m) 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; } @@ -2832,12 +3292,31 @@ mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAdd 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. @@ -2896,50 +3375,7 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco // 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 @@ -3333,25 +3769,15 @@ mDNSlocal void CheckCacheExpiration(mDNS *const m, const mDNSu32 slot, CacheGrou 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)); @@ -3392,35 +3818,75 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) 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; @@ -3443,6 +3909,7 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) 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 } @@ -3453,6 +3920,20 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) // 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)); @@ -3480,9 +3961,11 @@ exit: } // 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) @@ -3495,16 +3978,40 @@ mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m) 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 + } } } @@ -3695,20 +4202,77 @@ mDNSlocal void CheckProxyRecords(mDNS *const m, AuthRecord *list) 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) @@ -3719,6 +4283,8 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) { int i; AuthRecord *head, *tail; + mDNSu32 slot; + AuthGroup *ag; verbosedebugf("mDNS_Execute"); @@ -3738,7 +4304,7 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) // 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++) { @@ -3799,26 +4365,18 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) { 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"); @@ -3871,6 +4429,31 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) 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); @@ -3919,6 +4502,7 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const 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); @@ -3958,6 +4542,27 @@ mDNSlocal void SuspendLLQs(mDNS *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 @@ -3998,7 +4603,8 @@ mDNSlocal void ActivateUnicastQuery(mDNS *const m, DNSQuestion *const question, 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; @@ -4007,6 +4613,115 @@ mDNSlocal void ActivateUnicastQuery(mDNS *const m, DNSQuestion *const question, } } +// 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; @@ -4048,6 +4763,9 @@ mDNSexport void mDNS_UpdateAllowSleep(mDNS *const m) { #ifndef IDLESLEEPCONTROL_DISABLED mDNSBool allowSleep = mDNStrue; + char reason[128]; + + reason[0] = 0; if (m->SystemSleepOnlyIfWakeOnLAN) { @@ -4055,6 +4773,7 @@ mDNSexport void mDNS_UpdateAllowSleep(mDNS *const m) 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); } @@ -4064,12 +4783,13 @@ mDNSexport void mDNS_UpdateAllowSleep(mDNS *const m) 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; } @@ -4078,20 +4798,21 @@ mDNSexport void mDNS_UpdateAllowSleep(mDNS *const m) 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; @@ -4111,7 +4832,8 @@ mDNSlocal void SendSPSRegistration(mDNS *const m, NetworkInterfaceInfo *intf, co 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) { @@ -4124,40 +4846,48 @@ mDNSlocal void SendSPSRegistration(mDNS *const m, NetworkInterfaceInfo *intf, co 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) @@ -4165,8 +4895,6 @@ mDNSlocal void SendSPSRegistration(mDNS *const m, NetworkInterfaceInfo *intf, co else { mStatus err; - // Once we've attempted to register, we need to include our OWNER option in our packets when we re-awaken - m->SentSleepProxyRegistration = mDNStrue; LogSPS("SendSPSRegistration: Sending Update %s %d (%d) id %5d with %d records %d bytes to %#a:%d", intf->ifname, intf->NextSPSAttempt, sps, mDNSVal16(m->omsg.h.id), m->omsg.h.mDNS_numUpdates, p - m->omsg.data, &intf->SPSAddr[sps], mDNSVal16(intf->SPSPort[sps])); @@ -4190,6 +4918,31 @@ exit: 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) { @@ -4428,11 +5181,6 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) { m->SleepState = SleepState_Awake; m->SleepSeqNum++; - if (m->SentSleepProxyRegistration) // Include OWNER option in packets for 60 seconds after waking - { - m->SentSleepProxyRegistration = mDNSfalse; - m->AnnounceOwner = NonZeroTime(m->timenow + 60 * mDNSPlatformOneSecond); - } // If the machine wakes and then immediately tries to sleep again (e.g. a maintenance wake) // then we enforce a minimum delay of 16 seconds before we begin sleep processing. // This is to allow time for the Ethernet link to come up, DHCP to get an address, mDNS to issue queries, etc., @@ -5982,7 +6730,7 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // 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 @@ -5996,7 +6744,7 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // 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 @@ -6295,6 +7043,17 @@ exit: 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 @@ -6306,21 +7065,20 @@ exit: // 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 @@ -6410,16 +7168,29 @@ exit: } } +// 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); } @@ -6431,11 +7202,17 @@ mDNSlocal void SPSRecordCallback(mDNS *const m, AuthRecord *const ar, mStatus re if (result == mStatus_NameConflict) { - LogMsg("Received Conflicting mDNS -- waking %s %.6a %s", InterfaceNameForID(m, ar->resrec.InterfaceID), &ar->WakeUp.HMAC, ARDisplayString(m, ar)); - 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); @@ -6534,7 +7311,7 @@ mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m, 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; @@ -6577,7 +7354,7 @@ mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m, } 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); @@ -6594,7 +7371,6 @@ mDNSlocal void mDNSCoreReceiveUpdateR(mDNS *const m, const DNSMessage *const msg { 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) @@ -6614,15 +7390,30 @@ mDNSlocal void mDNSCoreReceiveUpdateR(mDNS *const m, const DNSMessage *const msg 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. @@ -6829,10 +7620,25 @@ mDNSlocal DNSQuestion *FindDuplicateQuestion(const mDNS *const m, const DNSQuest 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; @@ -6886,6 +7692,51 @@ mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, DNSQuestion *const questi 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) @@ -6939,14 +7790,42 @@ mDNSlocal int BetterMatchForName(const domainname *name, int namecount, const do 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) @@ -6956,6 +7835,17 @@ mDNSexport void SetValidDNSServers(mDNS *m, DNSQuestion *question) 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)) { @@ -6969,16 +7859,22 @@ mDNSexport void SetValidDNSServers(mDNS *m, DNSQuestion *question) { 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 @@ -7186,15 +8082,12 @@ mDNSlocal mDNSBool ShouldSuppressQuery(mDNS *const m, domainname *qname, mDNSu16 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) @@ -7202,15 +8095,15 @@ mDNSlocal void CheckSuppressedCurrentQuestion(mDNS *const m, DNSQuestion *q) // 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--; @@ -7225,11 +8118,11 @@ mDNSlocal void CheckSuppressedCurrentQuestion(mDNS *const m, DNSQuestion *q) // 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); @@ -7240,7 +8133,6 @@ mDNSlocal void CheckSuppressedCurrentQuestion(mDNS *const m, DNSQuestion *q) 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) @@ -7251,10 +8143,72 @@ 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, @@ -7262,38 +8216,45 @@ mDNSexport void CheckSuppressUnusableQuestions(mDNS *const m) // 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. // @@ -7308,8 +8269,8 @@ mDNSexport void CheckSuppressUnusableQuestions(mDNS *const m) // 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. // @@ -7343,8 +8304,8 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu { 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; } @@ -7410,6 +8371,7 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu 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() @@ -7447,7 +8409,8 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu question->id = zeroOpaque64; question->validDNSServers = zeroOpaque64; question->triedAllServersOnce = 0; - question->noServerResponse = 0; + question->noServerResponse = 0; + question->StopTime = 0; if (question->WakeOnResolve) { question->WakeOnResolveCount = InitialWakeOnResolveCount; @@ -7490,21 +8453,28 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu // 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)); } @@ -7527,6 +8497,12 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu #endif } + else + { + if (question->TimeoutQuestion) + question->StopTime = NonZeroTime(m->timenow + GetTimeoutForMcastQuestion(m, question) * mDNSPlatformOneSecond); + } + if (question->StopTime) SetNextQueryStopTime(m, question); SetNextQueryTime(m,question); } @@ -7618,6 +8594,13 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que 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; @@ -7759,7 +8742,12 @@ mDNSlocal mStatus mDNS_StartBrowse_internal(mDNS *const m, DNSQuestion *const qu 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); @@ -7933,7 +8921,12 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, 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; @@ -7948,7 +8941,12 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, 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; @@ -7963,7 +8961,12 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, 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; @@ -7978,7 +8981,12 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, 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; @@ -8029,7 +9037,12 @@ mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, m 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); @@ -8082,7 +9095,7 @@ mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newt 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 @@ -8092,8 +9105,8 @@ mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newt } #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 { @@ -8149,9 +9162,9 @@ mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) 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; @@ -8403,10 +9416,10 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s 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); @@ -8422,7 +9435,7 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s { 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); } @@ -8456,7 +9469,7 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s // 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. @@ -8481,13 +9494,20 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s // See 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, @@ -8538,12 +9558,7 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s 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); @@ -8744,10 +9759,13 @@ mDNSexport 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) + 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; @@ -8757,16 +9775,31 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, 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 @@ -8801,7 +9834,7 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, 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; @@ -8834,6 +9867,14 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, // 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 @@ -8851,14 +9892,25 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, } 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); @@ -8930,7 +9982,7 @@ mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordS 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 @@ -8939,7 +9991,7 @@ mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordS { 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); @@ -9016,9 +10068,20 @@ mDNSexport mStatus mDNS_DeregisterService_drt(mDNS *const m, ServiceRecordSet *s 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; @@ -9031,7 +10094,15 @@ mDNSexport mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const r 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)); @@ -9454,7 +10525,7 @@ mDNSlocal void SleepProxyServerCallback(mDNS *const m, ServiceRecordSet *const s (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"); } @@ -9573,6 +10644,7 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, 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; @@ -9580,7 +10652,6 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->SleepState = SleepState_Awake; m->SleepSeqNum = 0; m->SystemWakeOnLANEnabled = mDNSfalse; - m->SentSleepProxyRegistration = mDNSfalse; m->AnnounceOwner = NonZeroTime(timenow + 60 * mDNSPlatformOneSecond); m->DelaySleep = 0; m->SleepLimit = 0; @@ -9591,6 +10662,7 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->CurrentQuestion = mDNSNULL; m->LocalOnlyQuestions = mDNSNULL; m->NewLocalOnlyQuestions = mDNSNULL; + m->RestartQuestion = mDNSNULL; m->rrcache_size = 0; m->rrcache_totalused = 0; m->rrcache_active = 0; @@ -9604,6 +10676,10 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, } 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; @@ -9614,6 +10690,7 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->ResourceRecords = mDNSNULL; m->DuplicateRecords = mDNSNULL; m->NewLocalRecords = mDNSNULL; + m->NewLocalOnlyRecords = mDNSfalse; m->CurrentRecord = mDNSNULL; m->HostInterfaces = mDNSNULL; m->ProbeFailTime = 0; @@ -9948,16 +11025,16 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) 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); @@ -9967,8 +11044,34 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const 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 @@ -9994,7 +11097,10 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) 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 diff --git a/mDNSCore/mDNSEmbeddedAPI.h b/mDNSCore/mDNSEmbeddedAPI.h index 26aa0a8..3d6c12a 100755 --- a/mDNSCore/mDNSEmbeddedAPI.h +++ b/mDNSCore/mDNSEmbeddedAPI.h @@ -348,6 +348,7 @@ enum 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 @@ -406,6 +407,13 @@ typedef struct { mDNSu8 c[256]; } UTF8str255; // Null-terminated C string #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. @@ -424,6 +432,7 @@ typedef struct AuthRecord_struct AuthRecord; 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; @@ -1036,6 +1045,21 @@ enum 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; @@ -1050,6 +1074,7 @@ typedef struct DNSServer 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 @@ -1108,6 +1133,45 @@ typedef enum 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(), @@ -1134,6 +1198,7 @@ struct AuthRecord_struct 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 @@ -1218,12 +1283,21 @@ struct AuthRecord_struct #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 { @@ -1242,6 +1316,7 @@ struct CacheGroup_struct // Header object for a list of CacheRecords with the mDNSu8 namestorage[InlineCacheGroupNameSize]; }; + struct CacheRecord_struct { CacheRecord *next; // Next in list; first element of structure for efficiency reasons @@ -1401,7 +1476,7 @@ typedef struct DomainAuthInfo { 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 @@ -1410,6 +1485,8 @@ typedef struct DomainAuthInfo 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 @@ -1452,7 +1529,10 @@ struct DNSQuestion_struct 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; @@ -1461,7 +1541,7 @@ struct DNSQuestion_struct 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 @@ -1494,7 +1574,13 @@ struct DNSQuestion_struct 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; }; @@ -1557,6 +1643,7 @@ struct ZoneData_struct 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 { @@ -1575,6 +1662,7 @@ typedef struct DNameListElem typedef struct ClientTunnel { struct ClientTunnel *next; + const char *prefix; domainname dstname; mDNSBool MarkedForDeletion; mDNSv6Addr loc_inner; @@ -1636,23 +1724,23 @@ struct NetworkInterfaceInfo_struct 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; @@ -1676,9 +1764,8 @@ typedef void mDNSCallback(mDNS *const m, mStatus result); 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 // + mDNS_KnownBug_LimitedIPv6 = 1, + mDNS_KnownBug_LossySyslog = 2 // }; enum @@ -1747,12 +1834,15 @@ struct mDNS_struct 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 @@ -1761,6 +1851,8 @@ struct mDNS_struct 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 @@ -1770,8 +1862,9 @@ struct mDNS_struct 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; @@ -1782,6 +1875,7 @@ struct mDNS_struct 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 @@ -1804,7 +1898,7 @@ struct mDNS_struct 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 @@ -2038,6 +2132,7 @@ extern mStatus mDNS_Init (mDNS *const m, mDNS_PlatformSupport *const p, 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) @@ -2109,14 +2204,21 @@ typedef enum { mDNS_Dereg_normal, mDNS_Dereg_rapid, mDNS_Dereg_conflict, mDNS_De // 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); @@ -2125,7 +2227,7 @@ extern mStatus mDNS_DeregisterService_drt(mDNS *const m, ServiceRecordSet *sr, m 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, @@ -2163,7 +2265,7 @@ extern mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID I 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 @@ -2325,9 +2427,6 @@ extern mDNSBool mDNSv4AddrIsRFC1918(mDNSv4Addr *addr); // returns true for RFC1 #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 - @@ -2341,10 +2440,11 @@ extern mDNSBool mDNSv4AddrIsRFC1918(mDNSv4Addr *addr); // returns true for RFC1 // 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); @@ -2368,13 +2468,15 @@ 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 @@ -2489,7 +2591,7 @@ mDNSexport void mDNSASLLog(uuid_t *uuid, const char *subdomain, const char *resu // 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. @@ -2542,8 +2644,9 @@ extern void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, m 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. @@ -2599,7 +2702,11 @@ extern void mDNSCoreInitComplete(mDNS *const m, mStatus result); 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); @@ -2618,9 +2725,18 @@ extern void MakeNegativeCacheRecord(mDNS *const m, CacheRecord *const cr, 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 @@ -2631,7 +2747,6 @@ extern void SetupLocalAutoTunnelInterface_internal(mDNS *const m, mDNSBool servi 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 @@ -2753,6 +2868,17 @@ extern mDNSBool RecordReadyForSleep(mDNS *const m, AuthRecord *rr); // 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) @@ -2813,17 +2939,17 @@ struct CompileTimeAssertionChecks_mDNS 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 }; diff --git a/mDNSCore/uDNS.c b/mDNSCore/uDNS.c index 5d0facf..513150b 100755 --- a/mDNSCore/uDNS.c +++ b/mDNSCore/uDNS.c @@ -20,6 +20,9 @@ * Any dynamic run-time requirements should be handled by the platform layer below or client layer above */ +#if APPLE_OSX_mDNSResponder +#include +#endif #include "uDNS.h" #if(defined(_MSC_VER)) @@ -98,7 +101,7 @@ mDNSlocal void SetRecordRetry(mDNS *const m, AuthRecord *rr, mDNSu32 random) #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; @@ -146,6 +149,7 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons (*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; } @@ -345,17 +349,25 @@ mDNSexport DomainAuthInfo *GetAuthInfoForName(mDNS *m, const domainname *const n // 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) @@ -701,7 +713,7 @@ mDNSlocal mDNSu8 *putLLQ(DNSMessage *const msg, mDNSu8 *ptr, const DNSQuestion * // !!!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); @@ -1575,7 +1587,12 @@ mDNSlocal mStatus GetZoneData_StartQuery(mDNS *const m, ZoneData *zd, mDNSu16 qt 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; @@ -1604,15 +1621,46 @@ mDNSexport ZoneData *StartGetZoneData(mDNS *const m, const domainname *const nam 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 @@ -1668,7 +1716,7 @@ mDNSlocal void UpdateAllServiceRecords(mDNS *const m, AuthRecord *rr, mDNSBool r } 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 @@ -2053,7 +2101,7 @@ mDNSlocal void AdvertiseHostname(mDNS *m, HostnameInfo *h) { 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; @@ -2079,7 +2127,7 @@ mDNSlocal void AdvertiseHostname(mDNS *m, HostnameInfo *h) 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; @@ -2220,7 +2268,12 @@ mDNSlocal void GetStaticHostname(mDNS *m) 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; @@ -2959,7 +3012,7 @@ mDNSlocal mDNSBool SendGroupUpdates(mDNS *const m) } 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, @@ -4062,7 +4115,6 @@ mDNSlocal const mDNSu8 *mDNS_WABLabels[] = (const mDNSu8 *)"\002lb", (const mDNSu8 *)"\001r", (const mDNSu8 *)"\002dr", - (const mDNSu8 *)"\002cf", (const mDNSu8 *)mDNSNULL, }; @@ -4601,28 +4653,41 @@ mDNSexport void SleepRecordRegistrations(mDNS *m) } } -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) @@ -4631,95 +4696,6 @@ mDNSlocal void FreeARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus r 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 - 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; @@ -4743,7 +4719,7 @@ mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceR { 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); @@ -4790,7 +4766,7 @@ mDNSexport void udns_validatelists(void *const v) 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; @@ -4805,40 +4781,19 @@ mDNSexport void udns_validatelists(void *const v) } #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); @@ -4850,7 +4805,7 @@ mDNSexport mStatus uDNS_SetupSearchDomains(mDNS *const m, int action) 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; @@ -4858,10 +4813,10 @@ mDNSexport mStatus uDNS_SetupSearchDomains(mDNS *const m, int action) *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); @@ -4869,12 +4824,7 @@ mDNSexport mStatus uDNS_SetupSearchDomains(mDNS *const m, int action) 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 @@ -4893,14 +4843,15 @@ mDNSexport mStatus uDNS_SetupSearchDomains(mDNS *const m, int action) 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" @@ -4912,28 +4863,90 @@ mDNSexport mStatus uDNS_SetupSearchDomains(mDNS *const m, int action) 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: @@ -4952,5 +4965,5 @@ struct CompileTimeAssertionChecks_uDNS // 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]; }; diff --git a/mDNSCore/uDNS.h b/mDNSCore/uDNS.h index a06b9ae..2dfaf51 100755 --- a/mDNSCore/uDNS.h +++ b/mDNSCore/uDNS.h @@ -53,6 +53,11 @@ // 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); @@ -96,12 +101,11 @@ extern mStatus uDNS_SetupDNSConfig(mDNS *const m); // 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 { diff --git a/mDNSMacOS9/SubTypeTester.c b/mDNSMacOS9/SubTypeTester.c index 18b3899..e7e76d5 100644 --- a/mDNSMacOS9/SubTypeTester.c +++ b/mDNSMacOS9/SubTypeTester.c @@ -78,7 +78,7 @@ mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset, 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); diff --git a/mDNSMacOSX/BonjourEvents-Info.plist b/mDNSMacOSX/BonjourEvents-Info.plist new file mode 100644 index 0000000..6748eaa --- /dev/null +++ b/mDNSMacOSX/BonjourEvents-Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleName + ${PRODUCT_NAME} + CFBundleIconFile + + CFBundleIdentifier + com.apple.${PRODUCT_NAME:rfc1034Identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleSignature + ???? + CFBundleVersion + 1 + CFBundleShortVersionString + 1.0 + CFPlugInDynamicRegisterFunction + + CFPlugInDynamicRegistration + NO + CFPlugInFactories + + FB86416D-6164-2070-726F-70735C216EC0 + UserEventAgentFactory + + CFPlugInTypes + + FC86416D-6164-2070-726F-70735C216EC0 + + FB86416D-6164-2070-726F-70735C216EC0 + + + CFPlugInUnloadFunction + + LimitLoadToSessionType + + System + Aqua + + + diff --git a/mDNSMacOSX/BonjourEvents.c b/mDNSMacOSX/BonjourEvents.c new file mode 100644 index 0000000..6c5d700 --- /dev/null +++ b/mDNSMacOSX/BonjourEvents.c @@ -0,0 +1,1135 @@ +/* -*- 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 +#include +#include +#include +#include + + +#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 diff --git a/mDNSMacOSX/P2PPacketFilter.c b/mDNSMacOSX/P2PPacketFilter.c new file mode 100644 index 0000000..c61c7b1 --- /dev/null +++ b/mDNSMacOSX/P2PPacketFilter.c @@ -0,0 +1,297 @@ +/* + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#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; +} + diff --git a/mDNSMacOSX/P2PPacketFilter.h b/mDNSMacOSX/P2PPacketFilter.h new file mode 100644 index 0000000..81bd3df --- /dev/null +++ b/mDNSMacOSX/P2PPacketFilter.h @@ -0,0 +1,29 @@ +/* + * + * 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_ */ diff --git a/mDNSMacOSX/daemon.c b/mDNSMacOSX/daemon.c index 62d2f78..529a90c 100644 --- a/mDNSMacOSX/daemon.c +++ b/mDNSMacOSX/daemon.c @@ -84,12 +84,12 @@ static CacheEntity rrcachestorage[RR_CACHE_SIZE]; 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; @@ -109,6 +109,7 @@ static int started_via_launchdaemon = 0; // Indicates we're running on Tiger or 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 @@ -547,7 +548,7 @@ mDNSlocal mDNSBool CheckForExistingClient(mach_port_t c) 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) { @@ -567,11 +568,11 @@ mDNSlocal void ClientDeathCallback(CFMachPortRef unusedport, void *voidmsg, CFIn 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) { @@ -582,14 +583,14 @@ mDNSlocal void EnableDeathNotificationForClient(mach_port_t ClientMachPort, void 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 } //************************************************************************************************************* @@ -1077,7 +1078,7 @@ mDNSlocal mStatus AddServiceInstance(DNSServiceRegistration *x, const domainname 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; @@ -1401,7 +1402,7 @@ mDNSexport kern_return_t provide_DNSServiceRegistrationAddRecord_rpc(mach_port_t // 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) { @@ -1750,35 +1751,32 @@ mDNSlocal void ExitCallback(int sig) 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) { @@ -1789,8 +1787,9 @@ 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); @@ -1840,16 +1839,16 @@ mDNSlocal void INFOCallback(void) { // 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" : " ", @@ -1874,9 +1873,10 @@ mDNSlocal void INFOCallback(void) 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)" : @@ -1884,13 +1884,21 @@ mDNSlocal void INFOCallback(void) } } + LogMsgNoIdent("--------- Mcast Resolvers ----------"); + if (!mDNSStorage.McastResolvers) LogMsgNoIdent(""); + 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) { @@ -1946,8 +1954,7 @@ mDNSlocal kern_return_t mDNSDaemonInitialize(void) { 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) { @@ -1988,13 +1995,13 @@ mDNSlocal kern_return_t mDNSDaemonInitialize(void) 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) { @@ -2062,8 +2069,7 @@ mDNSlocal kern_return_t mDNSDaemonInitialize(void) { 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) { @@ -2125,13 +2131,25 @@ mDNSlocal kern_return_t mDNSDaemonInitialize(void) 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. @@ -2141,36 +2159,22 @@ mDNSlocal mDNSs32 mDNSDaemonIdle(mDNS *const m) 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 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) @@ -2403,32 +2407,38 @@ mDNSlocal mDNSBool AllowSleepNow(mDNS *const m, mDNSs32 now) 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)", @@ -2451,7 +2461,7 @@ mDNSlocal mDNSBool AllowSleepNow(mDNS *const m, mDNSs32 now) return(mDNStrue); } -#ifdef __LIB_DISPATCH__ +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM mDNSexport void TriggerEventCompletion() { @@ -2527,7 +2537,7 @@ mDNSlocal void PrepareForIdle(void *m_param) 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) { @@ -2680,7 +2690,7 @@ mDNSlocal void * KQueueLoop(void *m_param) return NULL; } -#endif __LIB_DISPATCH__ +#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM mDNSlocal void LaunchdCheckin(void) { @@ -2796,7 +2806,8 @@ mDNSexport int main(int argc, char **argv) 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)); @@ -2824,33 +2835,28 @@ mDNSexport int main(int argc, char **argv) if (!strcasecmp(argv[i], "-DebugLogging" )) mDNS_LoggingEnabled = mDNStrue; if (!strcasecmp(argv[i], "-UnicastPacketLogging" )) mDNS_PacketLoggingEnabled = mDNStrue; if (!strcasecmp(argv[i], "-OfferSleepProxyService" )) - OfferSleepProxyService = (i+1kqs.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) { diff --git a/mDNSMacOSX/helper-stubs.c b/mDNSMacOSX/helper-stubs.c index a1bd530..8cb7d58 100644 --- a/mDNSMacOSX/helper-stubs.c +++ b/mDNSMacOSX/helper-stubs.c @@ -224,16 +224,22 @@ fin: (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); @@ -244,16 +250,20 @@ fin: 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); @@ -272,3 +282,14 @@ void mDNSSendWakeupPacket(unsigned ifid, char *eth_addr, char *ip_addr, int iter 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; + } diff --git a/mDNSMacOSX/helper.c b/mDNSMacOSX/helper.c index a3a9046..73ceb3e 100644 --- a/mDNSMacOSX/helper.c +++ b/mDNSMacOSX/helper.c @@ -57,6 +57,7 @@ #include "helpermsgServer.h" #include "helper-server.h" #include "ipsec_options.h" +#include "P2PPacketFilter.h" #ifndef RTF_IFSCOPE #define RTF_IFSCOPE 0x1000000 @@ -571,9 +572,13 @@ static void update_notification(void) // 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 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); @@ -759,7 +764,7 @@ fin: 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. @@ -770,14 +775,15 @@ enum DNSKeyFormat 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 = { @@ -808,6 +814,7 @@ getDNSKeyFormat(SecKeychainItemRef item, SecKeychainAttributeList **attributesp) "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, @@ -829,6 +836,9 @@ getDNSKeyFormat(SecKeychainItemRef item, SecKeychainAttributeList **attributesp) 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; @@ -849,6 +859,7 @@ skip: return formatNotDNSKey; } +// Insert the attributes as defined by mDNSKeyChainAttributes static CFPropertyListRef getKeychainItemInfo(SecKeychainItemRef item, SecKeychainAttributeList *attributes, enum DNSKeyFormat format) @@ -865,6 +876,8 @@ getKeychainItemInfo(SecKeychainItemRef item, debug("CFArrayCreateMutable failed"); goto error; } + + // Insert the Account attribute (kmDNSKcWhere) switch ((enum DNSKeyFormat)format) { case formatDdnsTypeItem: @@ -872,9 +885,10 @@ getKeychainItemInfo(SecKeychainItemRef item, 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; @@ -886,6 +900,8 @@ getKeychainItemInfo(SecKeychainItemRef item, } CFArrayAppendValue(entry, data); CFRelease(data); + + // Insert the Where attribute (kmDNSKcAccount) if (NULL == (data = CFDataCreate(kCFAllocatorDefault, attributes->attr[2].data, attributes->attr[2].length))) { @@ -894,6 +910,8 @@ getKeychainItemInfo(SecKeychainItemRef item, } CFArrayAppendValue(entry, data); CFRelease(data); + + // Insert the Key attribute (kmDNSKcKey) if (noErr != (status = SecKeychainItemCopyAttributesAndData(item, NULL, NULL, NULL, &keylen, &keyp))) { @@ -911,6 +929,16 @@ getKeychainItemInfo(SecKeychainItemRef item, } 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: @@ -1408,7 +1436,7 @@ createAnonymousRacoonConfiguration(const char *fqdn) " 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" @@ -2146,7 +2174,7 @@ int 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[] = @@ -2158,8 +2186,8 @@ do_mDNSAutoTunnelSetKeys(__unused mach_port_t port, int replacedelete, " 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" @@ -2294,7 +2322,7 @@ do_mDNSAutoTunnelSetKeys(__unused mach_port_t port, int replacedelete, 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)) @@ -2343,7 +2371,7 @@ fin: 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 */ @@ -2439,3 +2467,44 @@ do_mDNSSendWakeupPacket(__unused mach_port_t port, unsigned ifid, const char *et 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; + } + diff --git a/mDNSMacOSX/helper.h b/mDNSMacOSX/helper.h index e9abade..dadf5d9 100644 --- a/mDNSMacOSX/helper.h +++ b/mDNSMacOSX/helper.h @@ -47,6 +47,17 @@ enum mDNSAutoTunnelSetKeysReplaceDelete 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 { @@ -69,10 +80,11 @@ extern void mDNSDynamicStoreSetConfig(int key, const char *subkey, CFPropertyLis 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 */ diff --git a/mDNSMacOSX/helpermsg.defs b/mDNSMacOSX/helpermsg.defs index 546621e..5026078 100644 --- a/mDNSMacOSX/helpermsg.defs +++ b/mDNSMacOSX/helpermsg.defs @@ -84,7 +84,7 @@ simpleroutine mDNSAutoTunnelInterfaceUpDown( 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; @@ -95,7 +95,7 @@ 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); @@ -106,3 +106,11 @@ simpleroutine mDNSSendWakeupPacket( 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); diff --git a/mDNSMacOSX/mDNSMacOSX.c b/mDNSMacOSX/mDNSMacOSX.c index a3f3ad3..dee00f3 100644 --- a/mDNSMacOSX/mDNSMacOSX.c +++ b/mDNSMacOSX/mDNSMacOSX.c @@ -66,6 +66,7 @@ #include // platform support for UTC time #include // for inet_aton #include +#include // for getaddrinfo #include // For IP_RECVTTL #ifndef IP_RECVTTL @@ -103,7 +104,7 @@ #include #include -#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); @@ -118,9 +119,12 @@ void IONotificationPortSetDispatchQueue(IONotificationPortRef notify, dispatch_q #include #include #include "helper.h" +#include "P2PPacketFilter.h" #include +#include + #if APPLE_OSX_mDNSResponder #include #include @@ -161,10 +165,11 @@ D2DStatus D2DRelease(D2DServiceInstance instanceHandle, D2DTransportType transpo // 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 @@ -188,7 +193,7 @@ static int HINFO_HWstring_prefixlen = 6; mDNSexport int WatchDogReportingThreshold = 250; -#ifdef __LIB_DISPATCH__ +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM dispatch_queue_t SSLqueue; #endif @@ -201,6 +206,10 @@ mDNSexport int ActiveDirectoryPrimaryDomainLabelCount; 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 - @@ -307,7 +316,7 @@ mDNSlocal mStatus DNSNameCompressionParseBytes(mDNS *const m, const mDNSu8 *cons { 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; @@ -478,7 +487,6 @@ mDNSlocal void xD2DAddToCache(mDNS *const m, D2DStatus result, D2DServiceInstanc mDNSPlatformMemFree(ptr); return; } - err = mDNS_Register(m, &ptr->ar); if (err) { @@ -698,6 +706,12 @@ mDNSexport void external_start_advertising_service(const ResourceRecord *const r 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"); @@ -885,7 +899,7 @@ mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(mDNS *const } -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); @@ -898,6 +912,9 @@ mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNS 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); @@ -910,7 +927,7 @@ mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNS #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); @@ -1054,7 +1071,14 @@ mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const ms 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 @@ -1417,13 +1441,13 @@ mDNSlocal OSStatus tlsSetupSock(TCPSocket *sock, mDNSBool server) 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. @@ -1527,7 +1551,7 @@ mDNSlocal mStatus spawnSSLHandshake(TCPSocket* sock) if (sock->handshake != handshake_required) LogMsg("spawnSSLHandshake: handshake status not required: %d", sock->handshake); sock->handshake = handshake_in_progress; KQueueSet(sock->fd, EV_DELETE, EVFILT_READ, sock->kqEntry); -#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);}); @@ -1592,7 +1616,7 @@ mDNSlocal void tcpKQSocketCallback(__unused int fd, short filter, void *context) 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(); @@ -1703,7 +1727,7 @@ mDNSexport void KQueueUnlock(mDNS *const m, const char const *task) 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) { @@ -1771,8 +1795,8 @@ mDNSlocal mStatus SetupTCPSocket(TCPSocket *sock, u_short sa_family, mDNSIPPort 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); @@ -1786,7 +1810,7 @@ mDNSlocal mStatus SetupTCPSocket(TCPSocket *sock, u_short sa_family, mDNSIPPort 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; @@ -2011,10 +2035,11 @@ mDNSexport void mDNSPlatformTCPCloseConnection(TCPSocket *sock) 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); @@ -2186,12 +2211,12 @@ mDNSlocal mStatus SetupSocket(KQSocketSet *cp, const mDNSIPPort port, u_short sa 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 @@ -2231,7 +2256,7 @@ mDNSlocal mStatus SetupSocket(KQSocketSet *cp, const mDNSIPPort port, u_short sa 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; @@ -2363,7 +2388,7 @@ mDNSexport void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSA 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 @@ -2415,7 +2440,7 @@ mDNSlocal void bpf_callback_common(NetworkInterfaceInfoOSX *info) 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); @@ -2647,7 +2672,8 @@ mDNSexport void mDNSPlatformReceiveBPF_fd(mDNS *const m, int fd) 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; @@ -2667,7 +2693,7 @@ mDNSexport void mDNSPlatformReceiveBPF_fd(mDNS *const m, int fd) { 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;} @@ -2966,7 +2992,13 @@ mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifad 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; @@ -3006,6 +3038,7 @@ mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifad // 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; @@ -3064,13 +3097,15 @@ static CFMutableDictionaryRef domainStatusDict = NULL; 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; } @@ -3159,11 +3194,14 @@ mDNSlocal void UpdateAutoTunnelDomainStatus(const mDNS *const m, const DomainAut 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) @@ -3174,7 +3212,8 @@ mDNSlocal void UpdateAutoTunnelDomainStatus(const mDNS *const m, const DomainAut 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; } @@ -3263,73 +3302,71 @@ mDNSlocal void UpdateAutoTunnelDomainStatus(const mDNS *const m, const DomainAut } } - // 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); @@ -3420,7 +3457,7 @@ mDNSlocal void UpdateAnonymousRacoonConfig(mDNS *m) // Determine whether we nee { 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); } } @@ -3683,7 +3720,6 @@ mDNSlocal void RegisterAutoTunnel6Record(mDNS *m, DomainAuthInfo *info) return; } - // if disabled administratively, don't register // if disabled administratively, don't register if (!m->RegisterAutoTunnel6 || DisableInboundRelayConnection) { @@ -3859,7 +3895,9 @@ mDNSlocal void AutoTunnelHostNameChanged(mDNS *m, DomainAuthInfo *info) { 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); @@ -3898,12 +3936,12 @@ mDNSlocal void SetupLocalAutoTunnel6Records(mDNS *const m, DomainAuthInfo *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); } @@ -3948,11 +3986,11 @@ mDNSexport void SetupLocalAutoTunnelInterface_internal(mDNS *const m, mDNSBool s 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; @@ -4003,7 +4041,7 @@ mDNSlocal mStatus AutoTunnelSetKeys(ClientTunnel *tun, mDNSBool AddNew) 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 @@ -4329,6 +4367,7 @@ mDNSexport void AddNewClientTunnel(mDNS *const m, DNSQuestion *const q) { 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; @@ -4352,7 +4391,12 @@ mDNSexport void AddNewClientTunnel(mDNS *const m, DNSQuestion *const q) 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; @@ -4379,6 +4423,12 @@ mDNSlocal mStatus UpdateInterfaceList(mDNS *const m, mDNSs32 utc) 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) @@ -4638,7 +4688,22 @@ mDNSlocal int SetupActiveInterfaces(mDNS *const m, mDNSs32 utc) // 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", @@ -4745,7 +4810,23 @@ mDNSlocal int ClearInactiveInterfaces(mDNS *const m, mDNSs32 utc) 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, @@ -4809,9 +4890,18 @@ mDNSlocal int compare_dns_configs(const void *aa, const void *bb) 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; @@ -4822,7 +4912,6 @@ mDNSlocal void ConfigResolvers(mDNS *const m, dns_config_t *config, mDNSBool sco 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 @@ -4839,19 +4928,20 @@ mDNSlocal void ConfigResolvers(mDNS *const m, dns_config_t *config, mDNSBool sco 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 @@ -4879,7 +4969,18 @@ mDNSlocal void ConfigResolvers(mDNS *const m, dns_config_t *config, mDNSBool sco // 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)) @@ -4919,11 +5020,10 @@ mDNSlocal void ConfigResolvers(mDNS *const m, dns_config_t *config, mDNSBool sco } } - // 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; @@ -4939,6 +5039,22 @@ mDNSlocal void ConfigResolvers(mDNS *const m, dns_config_t *config, mDNSBool sco } #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) { @@ -4951,17 +5067,27 @@ mDNSlocal void ConfigResolvers(mDNS *const m, dns_config_t *config, mDNSBool sco 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); } } } @@ -5019,7 +5145,7 @@ mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDN 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; } @@ -5036,7 +5162,7 @@ mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDN // 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 @@ -5066,6 +5192,7 @@ mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDN 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; } @@ -5203,7 +5330,7 @@ mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDN 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); } } } @@ -5219,7 +5346,7 @@ mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDN { 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 @@ -5231,8 +5358,9 @@ mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDN // (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); } @@ -5566,7 +5694,7 @@ mDNSexport void SetDomainSecrets(mDNS *m) 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) @@ -5577,29 +5705,40 @@ mDNSexport void SetDomainSecrets(mDNS *m) // 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); @@ -5608,13 +5747,53 @@ mDNSexport void SetDomainSecrets(mDNS *m) 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; @@ -5636,7 +5815,8 @@ mDNSexport void SetDomainSecrets(mDNS *m) // 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; @@ -5647,8 +5827,10 @@ mDNSexport void SetDomainSecrets(mDNS *m) 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; @@ -5968,18 +6150,23 @@ mDNSlocal void SetSPS(mDNS *const m) 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); } @@ -6013,7 +6200,7 @@ mDNSlocal mStatus WatchForInternetSharingChanges(mDNS *const m) 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 @@ -6079,8 +6266,7 @@ mDNSlocal mDNSu16 GetPortArray(mDNS *const m, int trans, mDNSIPPort *portarray) } #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) { @@ -6169,6 +6355,8 @@ mDNSexport mStatus ActivateLocalProxy(mDNS *const m, char *ifname) // Called wit 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; @@ -6266,7 +6454,7 @@ mDNSlocal mDNSBool SystemWakeForNetworkAccess(void) mDNSlocal mDNSBool SystemSleepOnlyIfWakeOnLAN(void) { mDNSs32 val = 0; - GetCurrentPMSetting(CFSTR("Idle Sleep Requires Network Proxy"), &val); + GetCurrentPMSetting(CFSTR("PrioritizeNetworkReachabilityOverSleep"), &val); return val != 0 ? mDNStrue : mDNSfalse; } @@ -6496,6 +6684,9 @@ mDNSlocal void ParseBackToMyMac(mDNS *const m, CFDictionaryRef connd) // 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; } @@ -6513,9 +6704,13 @@ mDNSlocal void ParseBackToMyMac(mDNS *const m, CFDictionaryRef connd) 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; @@ -6629,6 +6824,16 @@ mDNSlocal void SetNetworkChanged(mDNS *const m, mDNSs32 delay) } } +// 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//IPv4 @@ -6767,6 +6972,7 @@ mDNSlocal mDNSBool ChangedKeysHaveIPv4LL(CFArrayRef inkeys) 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); @@ -6781,6 +6987,24 @@ mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, v 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 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; @@ -6849,7 +7135,7 @@ mDNSlocal mStatus WatchForNetworkChanges(mDNS *const m) 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 @@ -6857,6 +7143,7 @@ mDNSlocal mStatus WatchForNetworkChanges(mDNS *const m) 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; @@ -6904,6 +7191,88 @@ mDNSlocal mStatus WatchForPMChanges(mDNS *const m) #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 ... + 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; @@ -6948,6 +7317,44 @@ mDNSlocal void SysEventCallBack(int s1, short __unused filter, void *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); @@ -6999,7 +7406,8 @@ mDNSlocal OSStatus KeychainChanged(SecKeychainEvent keychainEvent, SecKeychainCa 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); } } @@ -7013,7 +7421,20 @@ mDNSlocal OSStatus KeychainChanged(SecKeychainEvent keychainEvent, SecKeychainCa // 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"); } @@ -7032,15 +7453,13 @@ mDNSlocal void PowerOn(mDNS *const m) { 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; } } } @@ -7057,18 +7476,18 @@ mDNSlocal void PowerChanged(void *refcon, io_service_t service, natural_t messag 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) { @@ -7085,22 +7504,20 @@ mDNSlocal void PowerChanged(void *refcon, io_service_t service, natural_t messag 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) @@ -7111,7 +7528,7 @@ mDNSlocal void PowerChanged(void *refcon, io_service_t service, natural_t messag } 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; @@ -7171,6 +7588,773 @@ mDNSlocal void SnowLeopardPowerChanged(void *refcon, IOPMConnection connection, } #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 @@ -7186,7 +8370,7 @@ CF_EXPORT const CFStringRef _kCFSystemVersionBuildVersionKey; // 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]="", prodvers[256]="", buildver[256]=""; @@ -7204,7 +8388,10 @@ mDNSexport int mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring) } 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. @@ -7247,7 +8434,7 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) 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. @@ -7273,11 +8460,13 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) // 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); @@ -7330,7 +8519,7 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) 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; @@ -7405,11 +8594,11 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) 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 */ } } @@ -7427,6 +8616,7 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) 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", @@ -7438,7 +8628,7 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) 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__ @@ -7448,6 +8638,7 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) #endif if (SSLqueue == mDNSNULL) LogMsg("dispatch_queue_create: SSL queue NULL"); #endif + mDNSMacOSXUpdateEtcHosts(m); return(mStatus_NoError); } @@ -7492,7 +8683,7 @@ mDNSexport void mDNSPlatformClose(mDNS *const m) { 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); @@ -7507,7 +8698,7 @@ mDNSexport void mDNSPlatformClose(mDNS *const m) 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 @@ -7562,7 +8753,7 @@ mDNSexport void mDNSPlatformClose(mDNS *const m) { AnonymousRacoonConfig = mDNSNULL; LogInfo("mDNSPlatformClose: Deconfiguring autotunnel"); - (void)mDNSConfigureServer(kmDNSDown, mDNSNULL); + (void)mDNSConfigureServer(kmDNSDown, mDNSNULL, mDNSNULL); } if (m->AutoTunnelHostAddrActive && m->AutoTunnelHostAddr.b[0]) @@ -7627,14 +8818,12 @@ mDNSexport mDNSs32 mDNSPlatformRawTime(void) 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; @@ -7659,7 +8848,7 @@ mDNSexport void * mDNSPlatformMemAllocate(mDNSu32 len) { return(mallocL("mDNSP #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) { @@ -7669,8 +8858,12 @@ mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep) } 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 } } @@ -7679,7 +8872,7 @@ mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID Inte mDNSu32 ifindex; // Sanity check - ifindex = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID); + ifindex = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNStrue); if (ifindex <= 0) { LogMsg("mDNSPlatformSendWakeupPacket: ERROR!! Invalid InterfaceID %u", ifindex); @@ -7687,3 +8880,15 @@ mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID Inte } 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; + } diff --git a/mDNSMacOSX/mDNSMacOSX.h b/mDNSMacOSX/mDNSMacOSX.h index 522565e..c914415 100644 --- a/mDNSMacOSX/mDNSMacOSX.h +++ b/mDNSMacOSX/mDNSMacOSX.h @@ -30,8 +30,8 @@ #include #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 #endif @@ -43,7 +43,7 @@ typedef struct 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; @@ -91,7 +91,7 @@ struct NetworkInterfaceInfoOSX_struct 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; @@ -116,13 +116,7 @@ struct mDNS_PlatformSupport_struct mDNSs32 NotifyUser; mDNSs32 HostNameConflict; // Time we experienced conflict on our link-local host name mDNSs32 NetworkChanged; - - // KeyChain frequently fails to notify clients of change events. To work around this - // we set a timer and periodically poll to detect if any changes have occurred. - // Without this Back To My Mac just does't work for a large number of users. - // See Not getting Keychain Changed events when enabling BTMM - mDNSs32 KeyChainBugTimer; - mDNSs32 KeyChainBugInterval; + mDNSs32 KeyChainTimer; CFRunLoopRef CFRunLoop; SCDynamicStoreRef Store; @@ -141,7 +135,7 @@ struct mDNS_PlatformSupport_struct 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 @@ -156,8 +150,9 @@ struct mDNS_PlatformSupport_struct 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 @@ -172,10 +167,10 @@ extern int KQueueFD; extern void NotifyOfElusiveBug(const char *title, const char *msg); // Both strings are UTF-8 text extern void SetDomainSecrets(mDNS *m); extern void mDNSMacOSXNetworkChanged(mDNS *const m); -extern int mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring); +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 diff --git a/mDNSMacOSX/mDNSResponder.sb b/mDNSMacOSX/mDNSResponder.sb index 323ba3d..4a51b87 100644 --- a/mDNSMacOSX/mDNSResponder.sb +++ b/mDNSMacOSX/mDNSResponder.sb @@ -88,6 +88,8 @@ (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$")) @@ -129,12 +131,12 @@ ; 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(/|$)")) diff --git a/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj b/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj index 88c0d94..d03b50d 100644 --- a/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj +++ b/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ D284BF300ADD81630027CCDF /* PBXTargetDependency */, D284BF260ADD814F0027CCDF /* PBXTargetDependency */, D284BF2A0ADD81530027CCDF /* PBXTargetDependency */, + 216EF8EC120295AA004917D2 /* PBXTargetDependency */, ); name = "Build More"; productName = "Build All"; @@ -38,17 +39,43 @@ 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 */ = { @@ -58,7 +85,7 @@ ); dependencies = ( FFB7657D0AEED97F00583A2C /* PBXTargetDependency */, - FFA572710AF191230055A0F1 /* PBXTargetDependency */, + 2141DCFD123FFB7D0086D23E /* PBXTargetDependency */, ); name = "Build All"; productName = "Build All"; @@ -66,6 +93,26 @@ /* 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 */; }; @@ -104,6 +151,8 @@ 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, ); }; }; @@ -202,7 +251,6 @@ 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 */; }; @@ -279,6 +327,55 @@ 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 */; @@ -342,13 +439,6 @@ 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 */; @@ -416,16 +506,6 @@ ); 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; @@ -442,6 +522,12 @@ 000753D303367C1C0CCA2C71 /* mDNSMacOSX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mDNSMacOSX.h; sourceTree = ""; }; 00CA213D02786FC30CCA2C71 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = ""; }; 09AB6884FE841BABC02AAC07 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = ""; }; + 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 = ""; }; + 213FB22D12028B53002B3A08 /* BonjourEvents-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "BonjourEvents-Info.plist"; sourceTree = ""; }; + 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 = ""; }; 2E0405EB0C3190DC00F13B59 /* helpermsg.defs */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.mig; path = helpermsg.defs; sourceTree = ""; }; 2E0405F00C31955500F13B59 /* mDNSResponderHelper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mDNSResponderHelper; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -463,6 +549,8 @@ 4ADB5F230F6AB9F400B95BF3 /* helper-entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "helper-entitlements.plist"; sourceTree = ""; }; 4AE9B0480F39448B0080B59D /* safe_vproc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = safe_vproc.c; sourceTree = ""; }; 4AE9B0490F39448B0080B59D /* safe_vproc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = safe_vproc.h; sourceTree = ""; }; + 4BD2B638134FE09F002B96D5 /* P2PPacketFilter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = P2PPacketFilter.c; sourceTree = ""; }; + 4BD2B639134FE09F002B96D5 /* P2PPacketFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = P2PPacketFilter.h; sourceTree = ""; }; 654BE64F02B63B93000001D1 /* mDNSEmbeddedAPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mDNSEmbeddedAPI.h; path = ../mDNSCore/mDNSEmbeddedAPI.h; sourceTree = ""; }; 654BE65002B63B93000001D1 /* mDNSDebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mDNSDebug.h; path = ../mDNSCore/mDNSDebug.h; sourceTree = ""; }; 65713D46025A293200000109 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = /System/Library/Frameworks/SystemConfiguration.framework; sourceTree = ""; }; @@ -532,12 +620,12 @@ 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 = ""; }; FFA572600AF1908D0055A0F1 /* DNSServiceDiscovery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNSServiceDiscovery.h; sourceTree = ""; }; 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; }; @@ -554,6 +642,35 @@ /* 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; @@ -675,6 +792,7 @@ children = ( 08FB7795FE84155DC02AAC07 /* mDNS Server Sources */, 6575FC1F022EB78C00000109 /* Command-Line Clients */, + 213FB20912028902002B3A08 /* Bonjour Events Plugin */, 6575FBFE022EAFA800000109 /* MIG files */, DB2CC4420662DCE500335AB3 /* Java Support */, FFFB0DA407B43BED00B88D48 /* PreferencePane */, @@ -732,6 +850,8 @@ FFA572600AF1908D0055A0F1 /* DNSServiceDiscovery.h */, FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */, FFA572630AF190C20055A0F1 /* dns_sd.h */, + 4BD2B638134FE09F002B96D5 /* P2PPacketFilter.c */, + 4BD2B639134FE09F002B96D5 /* P2PPacketFilter.h */, ); name = "mDNS Server Sources"; sourceTree = ""; @@ -767,14 +887,27 @@ 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 = ""; }; + 213FB20912028902002B3A08 /* Bonjour Events Plugin */ = { + isa = PBXGroup; + children = ( + 213FB22C12028B53002B3A08 /* BonjourEvents.c */, + 213FB22D12028B53002B3A08 /* BonjourEvents-Info.plist */, + ); + name = "Bonjour Events Plugin"; + sourceTree = ""; + }; 6575FBFE022EAFA800000109 /* MIG files */ = { isa = PBXGroup; children = ( @@ -856,6 +989,27 @@ /* 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; @@ -867,6 +1021,7 @@ 2E8165E80C5980E300485EB2 /* libpfkey.h in Headers */, 2E8165EA0C5980F700485EB2 /* ipsec_strerror.h in Headers */, 4AE9B04C0F39448B0080B59D /* safe_vproc.h in Headers */, + 4BD2B63B134FE09F002B96D5 /* P2PPacketFilter.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -987,6 +1142,75 @@ /* 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" */; @@ -1172,9 +1396,9 @@ 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 */, @@ -1184,14 +1408,14 @@ ); 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 */, @@ -1201,29 +1425,28 @@ ); 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 */ @@ -1232,7 +1455,14 @@ 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 = ""; @@ -1250,15 +1480,28 @@ 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; @@ -1355,6 +1598,32 @@ 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; @@ -1366,7 +1635,7 @@ ); 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; @@ -1384,7 +1653,7 @@ ); 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; @@ -1406,7 +1675,7 @@ ); 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; @@ -1420,6 +1689,53 @@ /* 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; @@ -1429,6 +1745,7 @@ 2E96A51D0C39BDAC0087C4D2 /* helper-main.c in Sources */, 2E8165E90C5980EE00485EB2 /* pfkey.c in Sources */, 4AE9B04B0F39448B0080B59D /* safe_vproc.c in Sources */, + 4BD2B63A134FE09F002B96D5 /* P2PPacketFilter.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1607,6 +1924,41 @@ 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 */; @@ -1639,24 +1991,19 @@ }; 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 */; @@ -1702,6 +2049,101 @@ }; 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 = { @@ -1710,9 +2152,11 @@ 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}\""; @@ -1725,6 +2169,11 @@ "$(inherited)", "-lipsec", ); + "OTHER_LDFLAGS[sdk=iphoneos*] [arch=*]" = "-lipsec -Wl,-pie"; + "OTHER_LDFLAGS[sdk=macosx10.7][arch=*]" = ( + "-lipsec", + "-Wl,-pie", + ); PREBINDING = NO; PRODUCT_NAME = mDNSResponderHelper; }; @@ -1746,7 +2195,6 @@ 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 = ""; @@ -1762,6 +2210,7 @@ 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", @@ -1796,10 +2245,9 @@ 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, @@ -1821,11 +2269,13 @@ ); 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, @@ -1845,10 +2295,9 @@ 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 = ( @@ -1869,11 +2318,13 @@ ); 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, @@ -1889,6 +2340,7 @@ __text, mDNSResponder.order, ); + SKIP_INSTALL = YES; }; name = Development; }; @@ -1898,7 +2350,6 @@ 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"; @@ -1916,7 +2367,6 @@ 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; @@ -1938,7 +2388,6 @@ 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 = ( @@ -1966,7 +2415,6 @@ 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", @@ -1977,12 +2425,13 @@ 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; @@ -1998,7 +2447,6 @@ 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; @@ -2019,7 +2467,6 @@ 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; @@ -2041,6 +2488,7 @@ 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 = ( @@ -2048,8 +2496,15 @@ "__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; }; @@ -2059,6 +2514,7 @@ 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 = ( @@ -2067,8 +2523,15 @@ ); 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; }; @@ -2078,7 +2541,7 @@ COPY_PHASE_STRIP = NO; GCC_DYNAMIC_NO_PIC = NO; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - PRODUCT_NAME = SystemLibraries; + PRODUCT_NAME = SystemLibrariesDynamic; }; name = Development; }; @@ -2100,6 +2563,7 @@ 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 = ( @@ -2109,8 +2573,15 @@ 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; }; @@ -2125,6 +2596,54 @@ 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 = ( @@ -2221,7 +2740,7 @@ 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 */, @@ -2229,7 +2748,7 @@ 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 */, @@ -2237,7 +2756,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Development; }; - FFA5726E0AF191200055A0F1 /* Build configuration list for PBXAggregateTarget "SystemLibraries" */ = { + FFA5726E0AF191200055A0F1 /* Build configuration list for PBXAggregateTarget "SystemLibrariesDynamic" */ = { isa = XCConfigurationList; buildConfigurations = ( FFA5726F0AF191200055A0F1 /* Development */, @@ -2253,7 +2772,7 @@ 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 */, diff --git a/mDNSPosix/Identify.c b/mDNSPosix/Identify.c index 6bc9a24..2ca9fc6 100644 --- a/mDNSPosix/Identify.c +++ b/mDNSPosix/Identify.c @@ -221,7 +221,12 @@ mDNSlocal mStatus StartQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const m 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; diff --git a/mDNSPosix/Makefile b/mDNSPosix/Makefile index 9390f72..6ca9013 100755 --- a/mDNSPosix/Makefile +++ b/mDNSPosix/Makefile @@ -141,7 +141,7 @@ ifeq ($(os),x) # 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 diff --git a/mDNSPosix/NetMonitor.c b/mDNSPosix/NetMonitor.c index 771d52d..a10a4d7 100644 --- a/mDNSPosix/NetMonitor.c +++ b/mDNSPosix/NetMonitor.c @@ -478,7 +478,7 @@ mDNSlocal void DisplayPacketHeader(mDNS *const m, const DNSMessage *const msg, c 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); @@ -795,7 +795,7 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNS // 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); @@ -841,9 +841,8 @@ mDNSlocal mStatus mDNSNetMonitor(void) 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 diff --git a/mDNSPosix/ProxyResponder.c b/mDNSPosix/ProxyResponder.c index e91bcf3..ebbbaa7 100644 --- a/mDNSPosix/ProxyResponder.c +++ b/mDNSPosix/ProxyResponder.c @@ -69,8 +69,8 @@ mDNSlocal mStatus mDNS_RegisterProxyHost(mDNS *m, ProxyHost *p) { 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); @@ -158,7 +158,7 @@ mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset, 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); @@ -198,7 +198,7 @@ mDNSlocal void NoSuchServiceCallback(mDNS *const m, AuthRecord *const rr, mStatu 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); } @@ -213,7 +213,7 @@ mDNSlocal void RegisterNoSuchService(mDNS *m, AuthRecord *const rr, domainname * 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); } diff --git a/mDNSPosix/Responder.c b/mDNSPosix/Responder.c index 9e04dd3..21109b8 100755 --- a/mDNSPosix/Responder.c +++ b/mDNSPosix/Responder.c @@ -442,7 +442,7 @@ static mStatus RegisterOneService(const char * richTextName, 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; diff --git a/mDNSPosix/mDNSPosix.c b/mDNSPosix/mDNSPosix.c index 45aa621..3db5266 100755 --- a/mDNSPosix/mDNSPosix.c +++ b/mDNSPosix/mDNSPosix.c @@ -411,6 +411,26 @@ mDNSexport void mDNSPlatformTLSTearDownCerts(void) { } +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 @@ -482,7 +502,7 @@ mDNSexport int ParseDNSServers(mDNS *m, const char *filePath) 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++; } } @@ -522,9 +542,10 @@ mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(mDNS *const 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); @@ -1368,6 +1389,14 @@ mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID Inte (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; diff --git a/mDNSResponder.sln b/mDNSResponder.sln index 7413885..ed96501 100755 --- a/mDNSResponder.sln +++ b/mDNSResponder.sln @@ -73,7 +73,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ControlPanelLocRes", "mDNSW EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ControlPanelRes", "mDNSWindows\ControlPanel\ControlPanelRes.vcproj", "{5254AA9C-3D2E-4539-86D9-5EB0F4151215}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ControlPanel", "mDNSWindows\ControlPanel\ControlPanel.vcproj", "{F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ControlPanel", "mDNSWindows\ControlPanel\ControlPanel.vcproj", "{0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}" ProjectSection(ProjectDependencies) = postProject {3A2B6325-3053-4236-84BD-AA9BE2E323E5} = {3A2B6325-3053-4236-84BD-AA9BE2E323E5} EndProjectSection @@ -367,20 +367,20 @@ Global {5254AA9C-3D2E-4539-86D9-5EB0F4151215}.Release|Win32.Build.0 = Release|Win32 {5254AA9C-3D2E-4539-86D9-5EB0F4151215}.Release|x64.ActiveCfg = Release|x64 {5254AA9C-3D2E-4539-86D9-5EB0F4151215}.Release|x64.Build.0 = Release|x64 - {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Debug|Any CPU.ActiveCfg = Debug|x64 - {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Debug|Mixed Platforms.ActiveCfg = Debug|x64 - {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Debug|Mixed Platforms.Build.0 = Debug|x64 - {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Debug|Win32.ActiveCfg = Debug|Win32 - {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Debug|Win32.Build.0 = Debug|Win32 - {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Debug|x64.ActiveCfg = Debug|x64 - {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Debug|x64.Build.0 = Debug|x64 - {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Release|Any CPU.ActiveCfg = Release|x64 - {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Release|Mixed Platforms.ActiveCfg = Release|x64 - {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Release|Mixed Platforms.Build.0 = Release|x64 - {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Release|Win32.ActiveCfg = Release|Win32 - {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Release|Win32.Build.0 = Release|Win32 - {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Release|x64.ActiveCfg = Release|x64 - {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Release|x64.Build.0 = Release|x64 + {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|Any CPU.ActiveCfg = Debug|x64 + {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|Mixed Platforms.ActiveCfg = Debug|x64 + {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|Mixed Platforms.Build.0 = Debug|x64 + {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|Win32.ActiveCfg = Debug|Win32 + {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|Win32.Build.0 = Debug|Win32 + {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|x64.ActiveCfg = Debug|x64 + {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|x64.Build.0 = Debug|x64 + {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|Any CPU.ActiveCfg = Release|x64 + {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|Mixed Platforms.ActiveCfg = Release|x64 + {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|Mixed Platforms.Build.0 = Release|x64 + {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|Win32.ActiveCfg = Release|Win32 + {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|Win32.Build.0 = Release|Win32 + {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|x64.ActiveCfg = Release|x64 + {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|x64.Build.0 = Release|x64 {7826EA27-D4CC-4FAA-AD23-DF813823227B}.Debug|Any CPU.ActiveCfg = Debug|Win32 {7826EA27-D4CC-4FAA-AD23-DF813823227B}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 {7826EA27-D4CC-4FAA-AD23-DF813823227B}.Debug|Mixed Platforms.Build.0 = Debug|Win32 diff --git a/mDNSShared/PlatformCommon.c b/mDNSShared/PlatformCommon.c index a2c7409..7a1985a 100644 --- a/mDNSShared/PlatformCommon.c +++ b/mDNSShared/PlatformCommon.c @@ -129,7 +129,7 @@ mDNSexport void ReadDDNSSettingsFromConfFile(mDNS *const m, const char *const fi { 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); } diff --git a/mDNSShared/dns_sd.h b/mDNSShared/dns_sd.h index d2a9fdd..5416a60 100644 --- a/mDNSShared/dns_sd.h +++ b/mDNSShared/dns_sd.h @@ -77,7 +77,7 @@ */ #ifndef _DNS_SD_H -#define _DNS_SD_H 2582100 +#define _DNS_SD_H 3200500 #ifdef __cplusplus extern "C" { @@ -350,12 +350,27 @@ enum * 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(). */ @@ -493,7 +508,8 @@ enum 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) */ @@ -604,10 +620,10 @@ enum * 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 diff --git a/mDNSShared/dnsextd.c b/mDNSShared/dnsextd.c index ecce4fc..cba52d3 100644 --- a/mDNSShared/dnsextd.c +++ b/mDNSShared/dnsextd.c @@ -885,7 +885,7 @@ mDNSlocal mDNSu8 *PutUpdateSRV(DaemonInfo *d, DNSZone * zone, PktMsg *pkt, mDNSu ( 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; @@ -1645,7 +1645,7 @@ exit: 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); @@ -3104,9 +3104,9 @@ void mDNSCoreReceive(mDNS *const m, void *const msg, const mDNSu8 *const end, 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; } @@ -3127,8 +3127,8 @@ void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, const mDNSAd { ( 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() {} diff --git a/mDNSShared/dnssd_clientshim.c b/mDNSShared/dnssd_clientshim.c index a891915..3a4c629 100644 --- a/mDNSShared/dnssd_clientshim.c +++ b/mDNSShared/dnssd_clientshim.c @@ -264,7 +264,7 @@ DNSServiceErrorType DNSServiceRegister 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 @@ -509,6 +509,13 @@ DNSServiceErrorType DNSServiceResolve 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; @@ -523,7 +530,12 @@ DNSServiceErrorType DNSServiceResolve 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; @@ -648,6 +660,11 @@ DNSServiceErrorType DNSServiceQueryRecord 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; diff --git a/mDNSShared/dnssd_clientstub.c b/mDNSShared/dnssd_clientstub.c index fc43730..af182f7 100644 --- a/mDNSShared/dnssd_clientstub.c +++ b/mDNSShared/dnssd_clientstub.c @@ -31,6 +31,8 @@ #include "dnssd_ipc.h" +static int gDaemonErr = kDNSServiceErr_NoError; + #if defined(_WIN32) #define _SSIZE_T @@ -56,6 +58,7 @@ static int g_initWinsock = 0; #define LOG_WARNING kDebugLevelWarning + #define LOG_INFO kDebugLevelInfo static void syslog( int priority, const char * message, ...) { va_list args; @@ -88,6 +91,8 @@ // 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 @@ -196,6 +201,9 @@ static int read_all(dnssd_sock_t sd, char *buf, int len) 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; @@ -262,6 +270,39 @@ static int more_bytes(dnssd_sock_t sd) 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 @@ -353,7 +394,6 @@ static void FreeDNSServiceOp(DNSServiceOp *x) 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; @@ -612,7 +652,10 @@ static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr) #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; } @@ -626,6 +669,7 @@ static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr) // 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 @@ -711,10 +755,13 @@ static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr) // 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); @@ -1103,7 +1150,7 @@ DNSServiceErrorType DNSSD_API DNSServiceResolve { 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 diff --git a/mDNSShared/dnssd_ipc.h b/mDNSShared/dnssd_ipc.h index 760e1b4..d41edc8 100644 --- a/mDNSShared/dnssd_ipc.h +++ b/mDNSShared/dnssd_ipc.h @@ -40,6 +40,7 @@ # 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) @@ -63,6 +64,7 @@ extern char *win32_strerror(int inErrorCode); # 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 @@ -116,7 +118,7 @@ extern char *win32_strerror(int inErrorCode); 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, diff --git a/mDNSShared/uds_daemon.c b/mDNSShared/uds_daemon.c index 9ad7bc5..a2588e7 100644 --- a/mDNSShared/uds_daemon.c +++ b/mDNSShared/uds_daemon.c @@ -35,6 +35,11 @@ #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 @@ -145,6 +150,7 @@ struct request_state int unresponsiveness_reports; struct reply_state *replies; // corresponding (active) reply list req_termination_fn terminate; + DNSServiceFlags flags; union { @@ -180,7 +186,9 @@ struct request_state mDNSu32 flags; mDNSu32 protocol; DNSQuestion q4; + DNSQuestion *q42; DNSQuestion q6; + DNSQuestion *q62; } addrinfo; struct { @@ -198,7 +206,7 @@ struct request_state struct { DNSQuestion q; - DNSQuestion q2; + DNSQuestion *q2; } queryrecord; struct { @@ -415,7 +423,7 @@ mDNSlocal mStatus GenerateNTDResponse(const domainname *const servicename, const // 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 @@ -458,7 +466,7 @@ mDNSlocal void GenerateBonjourBrowserResponse(const domainname *const servicenam // 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 @@ -484,6 +492,10 @@ mDNSlocal AuthRecord *read_rr_from_ipc_msg(request_state *request, int GetTTL, i 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; } @@ -499,8 +511,19 @@ mDNSlocal AuthRecord *read_rr_from_ipc_msg(request_state *request, int GetTTL, i 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)) { @@ -590,6 +613,14 @@ mDNSlocal void external_start_advertising_helper(service_instance *const instanc 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++) @@ -611,9 +642,11 @@ mDNSlocal void external_stop_advertising_helper(service_instance *const instance 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); @@ -766,8 +799,11 @@ mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, m 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 } @@ -833,6 +869,12 @@ mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord *rr, mStatus result) 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); } } @@ -856,7 +898,7 @@ mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord *rr, mStatus result) 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); } @@ -874,8 +916,10 @@ mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord *rr, mStatus result) 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; } @@ -950,8 +994,18 @@ mDNSlocal mStatus handle_regrecord_request(request_state *request) 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; @@ -961,9 +1015,6 @@ mDNSlocal mStatus handle_regrecord_request(request_state *request) 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 @@ -974,6 +1025,17 @@ mDNSlocal mStatus handle_regrecord_request(request_state *request) 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); } @@ -1036,12 +1098,16 @@ mDNSlocal mStatus add_record_to_service(request_state *request, service_instance 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; } @@ -1065,7 +1131,12 @@ mDNSlocal mStatus handle_add_request(request_state *request) 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) @@ -1098,6 +1169,7 @@ mDNSlocal void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd 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: @@ -1165,6 +1237,10 @@ mDNSlocal mStatus handle_update_request(request_state *request) 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) { @@ -1345,7 +1421,7 @@ mDNSexport AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p) 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)) @@ -1362,8 +1438,19 @@ mDNSlocal mStatus register_service_instance(request_state *request, const domain 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 @@ -1415,7 +1502,7 @@ mDNSlocal mStatus register_service_instance(request_state *request, const domain 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) { @@ -1516,6 +1603,7 @@ mDNSlocal mStatus handle_regservice_request(request_state *request) 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; @@ -1600,8 +1688,8 @@ mDNSlocal mStatus handle_regservice_request(request_state *request) 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 @@ -1668,7 +1756,7 @@ bonjourbrowserhack: 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); } @@ -1699,10 +1787,11 @@ mDNSlocal mStatus add_domain_to_browser(request_state *info, const domainname *d 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); } } @@ -1715,10 +1804,11 @@ mDNSlocal void browse_termination_callback(request_state *info) { 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); } @@ -1808,7 +1898,7 @@ mDNSlocal void RegisterLocalOnlyDomainEnumPTR(mDNS *m, const domainname *d, int (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); @@ -1915,7 +2005,7 @@ mDNSlocal void UpdateDeviceInfoRecord(mDNS *const m) 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); @@ -2042,6 +2132,7 @@ mDNSlocal mStatus handle_browse_request(request_state *request) 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); @@ -2060,7 +2151,8 @@ mDNSlocal mStatus handle_browse_request(request_state *request) 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 @@ -2137,7 +2229,7 @@ mDNSlocal void resolve_result_callback(mDNS *const m, DNSQuestion *question, con // 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]; @@ -2175,6 +2267,7 @@ mDNSlocal mStatus handle_resolve_request(request_state *request) mDNSBool wasP2P = (interfaceIndex == kDNSServiceInterfaceIndexP2P); + request->flags = flags; if (wasP2P) interfaceIndex = kDNSServiceInterfaceIndexAny; InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); @@ -2204,7 +2297,12 @@ mDNSlocal mStatus handle_resolve_request(request_state *request) 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; @@ -2218,7 +2316,12 @@ mDNSlocal mStatus handle_resolve_request(request_state *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; @@ -2241,8 +2344,12 @@ mDNSlocal mStatus handle_resolve_request(request_state *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); + } } } @@ -2260,6 +2367,238 @@ mDNSlocal mStatus handle_resolve_request(request_state *request) // 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 ".bar.local." we always do a second unicast query in parallel. + // For names of the form ".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]; @@ -2268,37 +2607,160 @@ mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question, 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); @@ -2317,7 +2779,14 @@ mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question, 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]; @@ -2335,6 +2804,13 @@ mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question, 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) @@ -2412,10 +2888,48 @@ mDNSlocal void queryrecord_termination_callback(request_state *request) { 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) @@ -2437,6 +2951,7 @@ 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; @@ -2451,10 +2966,40 @@ mDNSlocal mStatus handle_queryrecord_request(request_state *request) 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); @@ -2462,46 +3007,15 @@ mDNSlocal mStatus handle_queryrecord_request(request_state *request) 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 ".bar.local." we always do a second unicast query in parallel. - // For names of the form ".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); @@ -2640,7 +3154,7 @@ mDNSlocal mStatus handle_reconfirm_request(request_state *request) "%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); @@ -2722,7 +3236,7 @@ mDNSlocal void port_mapping_create_request_callback(mDNS *m, NATTraversalInfo *n 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]; @@ -2811,12 +3325,54 @@ mDNSlocal void addrinfo_termination_callback(request_state *request) 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) @@ -2862,11 +3418,29 @@ 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); @@ -2875,11 +3449,26 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request) 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); @@ -2894,6 +3483,9 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request) 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", @@ -3007,7 +3599,7 @@ mDNSlocal void read_msg(request_state *req) 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) { @@ -3130,124 +3722,127 @@ mDNSlocal void request_callback(int fd, short filter, void *info) (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) @@ -3553,6 +4148,87 @@ mDNSlocal void LogClientInfo(mDNS *const m, const request_state *req) 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(""); + else if (truncated) LogMsgNoIdent("", 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(""); + } + mDNSlocal void LogAuthRecords(mDNS *const m, const mDNSs32 now, AuthRecord *ResourceRecords, int *proxy) { mDNSBool showheader = mDNStrue; @@ -3581,9 +4257,9 @@ mDNSlocal void LogAuthRecords(mDNS *const m, const mDNSs32 now, AuthRecord *Reso (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", @@ -3649,6 +4325,12 @@ mDNSexport void udsserver_info(mDNS *const m) 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); @@ -3736,7 +4418,7 @@ mDNSexport void udsserver_info(mDNS *const m) { 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 @@ -3746,8 +4428,8 @@ mDNSexport void udsserver_info(mDNS *const m) { 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 @@ -3776,15 +4458,17 @@ mDNSexport void udsserver_info(mDNS *const m) if (!AutoRegistrationDomains) LogMsgNoIdent(""); else for (d=AutoRegistrationDomains; d; d=d->next) LogMsgNoIdent("%##s", d->name.c); - LogMsgNoIdent("--- Search Domains --"); - if (!SearchList) LogMsgNoIdent(""); - else - { - for (s=SearchList; s; s=s->next) - { - LogMsgNoIdent("%##s", s->domain.c); - } - } + LogMsgNoIdent("--- Search Domains --"); + if (!SearchList) LogMsgNoIdent(""); + 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) @@ -3807,6 +4491,9 @@ mDNSexport void udsserver_info(mDNS *const m) LogMsgNoIdent("SPSProxyListChanged%s", m->SPSProxyListChanged ? "" : " "); LogMsgNoIdent("LocalRemoveEvents%s", m->LocalRemoveEvents ? "" : " "); + 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) @@ -3836,9 +4523,7 @@ mDNSexport void udsserver_info(mDNS *const m) 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 @@ -4022,10 +4707,10 @@ struct CompileTimeAssertionChecks_uds_daemon // 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]; }; diff --git a/mDNSShared/uds_daemon.h b/mDNSShared/uds_daemon.h index 93cfd1e..1e17efa 100644 --- a/mDNSShared/uds_daemon.h +++ b/mDNSShared/uds_daemon.h @@ -60,6 +60,7 @@ extern int CountPeerRegistrations(mDNS *const m, ServiceRecordSet *const srs); 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); diff --git a/mDNSWindows/DLL/dnssd.vcproj b/mDNSWindows/DLL/dnssd.vcproj index 1bfb5cc..814a322 100644 --- a/mDNSWindows/DLL/dnssd.vcproj +++ b/mDNSWindows/DLL/dnssd.vcproj @@ -55,6 +55,7 @@ BufferSecurityCheck="true" UsePrecompiledHeader="0" AssemblerListingLocation="$(IntDir)\" + ProgramDataBaseFileName="$(IntDir)\dnssd.dll.pdb" WarningLevel="4" Detect64BitPortabilityProblems="true" DebugInformationFormat="3" @@ -80,7 +81,7 @@ LinkIncremental="2" ModuleDefinitionFile="dnssd.def" GenerateDebugInformation="true" - ProgramDatabaseFile="$(OutDir)/dnssd.pdb" + ProgramDatabaseFile="$(OutDir)\dnssd.dll.pdb" SubSystem="2" BaseAddress="0x16000000" ImportLibrary="$(OutDir)\dnssd.lib" @@ -149,6 +150,7 @@ BufferSecurityCheck="true" UsePrecompiledHeader="0" AssemblerListingLocation="$(IntDir)\" + ProgramDataBaseFileName="$(IntDir)\dnssd.dll.pdb" WarningLevel="4" Detect64BitPortabilityProblems="true" DebugInformationFormat="3" @@ -174,7 +176,7 @@ LinkIncremental="2" ModuleDefinitionFile="dnssd.def" GenerateDebugInformation="true" - ProgramDatabaseFile="$(OutDir)/dnssd.pdb" + ProgramDatabaseFile="$(OutDir)\dnssd.dll.pdb" SubSystem="2" BaseAddress="0x16000000" ImportLibrary="$(OutDir)\dnssd.lib" @@ -235,6 +237,7 @@ RuntimeLibrary="0" UsePrecompiledHeader="0" AssemblerListingLocation="$(IntDir)\" + ProgramDataBaseFileName="$(IntDir)\dnssd.dll.pdb" WarningLevel="4" Detect64BitPortabilityProblems="true" DebugInformationFormat="3" @@ -260,7 +263,7 @@ LinkIncremental="1" ModuleDefinitionFile="dnssd.def" GenerateDebugInformation="true" - ProgramDatabaseFile="$(IntDir)/$(ProjectName).pdb" + ProgramDatabaseFile="$(OutDir)\dnssd.dll.pdb" SubSystem="2" OptimizeReferences="2" EnableCOMDATFolding="2" @@ -291,7 +294,7 @@ /> diff --git a/mDNSWindows/DLLStub/DLLStub.vcproj b/mDNSWindows/DLLStub/DLLStub.vcproj index 8029219..dc68289 100755 --- a/mDNSWindows/DLLStub/DLLStub.vcproj +++ b/mDNSWindows/DLLStub/DLLStub.vcproj @@ -51,6 +51,7 @@ BasicRuntimeChecks="3" RuntimeLibrary="1" UsePrecompiledHeader="0" + ProgramDataBaseFileName="$(IntDir)\dnssd.pdb" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="3" @@ -118,6 +119,7 @@ BasicRuntimeChecks="3" RuntimeLibrary="1" UsePrecompiledHeader="0" + ProgramDataBaseFileName="$(IntDir)\dnssd.pdb" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="3" @@ -181,6 +183,7 @@ PreprocessorDefinitions="WIN32;NDEBUG;_LIB;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1" RuntimeLibrary="0" UsePrecompiledHeader="0" + ProgramDataBaseFileName="$(IntDir)\dnssd.lib.pdb" WarningLevel="3" Detect64BitPortabilityProblems="true" DebugInformationFormat="3" @@ -212,7 +215,7 @@ /> diff --git a/mDNSWindows/SystemService/Service.c b/mDNSWindows/SystemService/Service.c index 2ef5d21..8b4c2c6 100644 --- a/mDNSWindows/SystemService/Service.c +++ b/mDNSWindows/SystemService/Service.c @@ -1189,6 +1189,7 @@ static OSStatus ServiceSpecificInitialize( int argc, LPTSTR argv[] ) 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); @@ -1305,11 +1306,27 @@ static OSStatus ServiceSpecificRun( int argc, LPTSTR argv[] ) } // 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 , 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 ) { @@ -2061,6 +2078,7 @@ mDNSlocal void UDSCanRead( TCPSocket * sock ) // udsSupportAddFDToEventLoop //=========================================================================================================================== + mStatus udsSupportAddFDToEventLoop( SocketRef fd, udsEventCallback callback, void *context, void **platform_data) { diff --git a/mDNSWindows/SystemService/Service.vcproj b/mDNSWindows/SystemService/Service.vcproj index 7461028..720f00a 100644 --- a/mDNSWindows/SystemService/Service.vcproj +++ b/mDNSWindows/SystemService/Service.vcproj @@ -44,7 +44,7 @@ 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. @@ -333,6 +334,7 @@ mDNSexport mStatus mDNSPlatformInit( mDNS * const inMDNS ) 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; @@ -364,6 +366,7 @@ mDNSexport mStatus mDNSPlatformInit( mDNS * const 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; @@ -488,21 +491,13 @@ mDNSexport void mDNSPlatformClose( mDNS * const 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 @@ -542,9 +537,10 @@ mDNSexport void mDNSPlatformClose( mDNS * const inMDNS ) // 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(); @@ -872,9 +868,10 @@ mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex( mDNS * con // 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 ) @@ -1013,10 +1010,10 @@ mDNSPlatformTCPConnect ) { struct sockaddr_in saddr; - ( void ) hostname; mStatus err = mStatus_NoError; DEBUG_UNUSED( inInterfaceID ); + ( void ) hostname; if ( inDstIP->type != mDNSAddrType_IPv4 ) { @@ -1118,15 +1115,13 @@ mDNSexport void mDNSPlatformTCPCloseConnection( TCPSocket *sock ) 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 //=========================================================================================================================== @@ -1158,26 +1153,13 @@ mDNSexport long mDNSPlatformReadTCP( TCPSocket *sock, void *inBuffer, unsigned l 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 { @@ -1237,6 +1219,10 @@ mStatus TCPAddSocket( mDNS * const inMDNS, TCPSocket *sock ) ( 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 ); @@ -1320,16 +1306,22 @@ mDNSlocal mStatus TCPBeginRecv( TCPSocket * sock ) 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; @@ -1346,83 +1338,25 @@ mDNSlocal void CALLBACK TCPEndRecv( DWORD error, DWORD bytesTransferred, LPWSAOV ( 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 ) ) - { - // mDNSResponder gets locking errors on Windows - // - // There seems to be a bug in WinSock with respect to Alertable I/O. According - // to MSDN , 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 @@ -1443,11 +1377,12 @@ mDNSexport UDPSocket* mDNSPlatformUDPSocket(mDNS *const m, const mDNSIPPort requ // 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 @@ -1485,9 +1420,9 @@ mDNSexport UDPSocket* mDNSPlatformUDPSocket(mDNS *const m, const mDNSIPPort requ // Bookkeeping - sock->next = gUDPSocketList; - gUDPSocketList = sock; - gUDPSockets++; + sock->next = gUDPSockets; + gUDPSockets = sock; + gUDPNumSockets++; exit: @@ -1506,7 +1441,7 @@ exit: mDNSexport void mDNSPlatformUDPClose( UDPSocket *sock ) { - UDPSocket * current = gUDPSocketList; + UDPSocket * current = gUDPSockets; UDPSocket * last = NULL; while ( current ) @@ -1515,7 +1450,7 @@ mDNSexport void mDNSPlatformUDPClose( UDPSocket *sock ) { if ( last == NULL ) { - gUDPSocketList = sock->next; + gUDPSockets = sock->next; } else { @@ -1533,13 +1468,11 @@ mDNSexport void mDNSPlatformUDPClose( UDPSocket *sock ) // 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; } @@ -1646,6 +1579,13 @@ mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID // mDNSPlatformSendRawPacket //=========================================================================================================================== +mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason) + { + DEBUG_UNUSED( m ); + DEBUG_UNUSED( allowSleep ); + DEBUG_UNUSED( reason ); + } + mDNSexport void mDNSPlatformSendRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID) { DEBUG_UNUSED( msg ); @@ -1662,6 +1602,7 @@ mDNSexport void mDNSPlatformReceiveRawPacket(const void *const msg, const mDNSu8 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 ); @@ -1796,6 +1737,15 @@ exit: } +mDNSexport void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result) + { + (void)m; // unused + (void)rr; + (void)result; + } + + + //=========================================================================================================================== // SetDomainSecrets //=========================================================================================================================== @@ -1868,7 +1818,7 @@ SetSearchDomainList( void ) while ( tok ) { if ( ( strcmp( tok, "" ) != 0 ) && ( strcmp( tok, "." ) != 0 ) ) - mDNS_AddSearchDomain_CString(tok); + mDNS_AddSearchDomain_CString(tok, mDNSNULL); tok = strtok( NULL, "," ); } @@ -1914,7 +1864,7 @@ SetReverseMapSearchDomainList( void ) 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); } } @@ -2001,7 +1951,7 @@ SetDNSServers( mDNS *const m ) { 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: @@ -2086,7 +2036,7 @@ SetDomainFromDHCP( void ) check_noerr( err ); } - if ( domain && domain[0] ) mDNS_AddSearchDomain_CString(domain); + if ( domain && domain[0] ) mDNS_AddSearchDomain_CString(domain, mDNSNULL); break; } @@ -2192,6 +2142,15 @@ mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID Inte (void) iteration; } +mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf) + { + (void) rr; + (void) intf; + + return 1; + } + + #if 0 #pragma mark - #endif @@ -2694,10 +2653,6 @@ mStatus TearDownInterfaceList( mDNS * const inMDNS ) 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. @@ -2755,12 +2710,13 @@ mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inI 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'; @@ -2891,12 +2847,8 @@ mDNSlocal mStatus TearDownInterface( mDNS * const inMDNS, mDNSInterfaceData *inI // 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. @@ -3184,22 +3136,25 @@ mDNSlocal mStatus SockAddrToMDNSAddr( const struct sockaddr * const inSA, mDNSAd 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; @@ -3209,13 +3164,13 @@ mDNSlocal OSStatus UDPBeginRecv( UDPSocket * sock ) { 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 ); // iTunes 9.1 fails to install with Bonjour service on Windows 7 Ultimate @@ -3231,7 +3186,7 @@ mDNSlocal OSStatus UDPBeginRecv( UDPSocket * sock ) { 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 ); } @@ -3248,6 +3203,8 @@ mDNSlocal OSStatus UDPBeginRecv( UDPSocket * sock ) } while ( ( ( err == WSAECONNRESET ) || ( err == WSAEFAULT ) ) && ( numTries++ < 100 ) ); + sock->overlapped.pending = TRUE; + exit: if ( err ) @@ -3268,179 +3225,117 @@ mDNSlocal void CALLBACK UDPEndRecv( DWORD err, DWORD bytesTransferred, LPWSAOVER 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. - // 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 ) ) + // 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 ); + } - // mDNSResponder gets locking errors on Windows - // - // There seems to be a bug in WinSock with respect to Alertable I/O. According - // to MSDN , 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; } @@ -4571,6 +4466,25 @@ exit: } +//=========================================================================================================================== +// 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 //=========================================================================================================================== @@ -4598,6 +4512,25 @@ TCPFreeSocket( TCPSocket *sock ) } +//=========================================================================================================================== +// 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 //=========================================================================================================================== @@ -4607,7 +4540,7 @@ UDPFreeSocket( UDPSocket * sock ) { 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 ) { @@ -4811,7 +4744,7 @@ mDNSlocal void SetDomainSecret( mDNS * const m, const domainname * inDomain ) 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); @@ -5150,118 +5083,85 @@ exit: } -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 ); -} diff --git a/mDNSWindows/mDNSWin32.h b/mDNSWindows/mDNSWin32.h index 95e1fdd..e08c462 100755 --- a/mDNSWindows/mDNSWin32.h +++ b/mDNSWindows/mDNSWin32.h @@ -31,6 +31,21 @@ 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 )(); @@ -43,13 +58,14 @@ struct TCPSocket_struct BOOL connected; TCPUserCallback userCallback; void * userContext; + Overlapped overlapped; DWORD lastError; BOOL closed; - 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; }; @@ -61,53 +77,19 @@ struct UDPSocket_struct // 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 @@ -216,7 +198,7 @@ extern mStatus TCPAddSocket( mDNS * const inMDNS, TCPSocket *sock ); 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 -- 2.47.2