From ee071a7a97699aaab1b6d1e93596ee5c8c31bf37 Mon Sep 17 00:00:00 2001 From: pettai Date: Mon, 31 Mar 2014 23:21:21 +0000 Subject: [PATCH] Import mDNSResponder-320.16, as previous import (258.14) is quite old --- .../mDNSResponder/dist/Clients/dns-sd.c | 72 +- .../mDNSResponder/dist/mDNSCore/DNSCommon.c | 170 +- .../mDNSResponder/dist/mDNSCore/DNSCommon.h | 4 +- .../mDNSResponder/dist/mDNSCore/DNSDigest.c | 2 +- .../mDNSResponder/dist/mDNSCore/mDNS.c | 1904 ++++++++++++++--- .../dist/mDNSCore/mDNSEmbeddedAPI.h | 228 +- .../mDNSResponder/dist/mDNSCore/uDNS.c | 423 ++-- .../mDNSResponder/dist/mDNSCore/uDNS.h | 16 +- .../dist/mDNSPosix/PosixDaemon.c | 76 +- .../mDNSResponder/dist/mDNSPosix/mDNSPosix.c | 42 +- .../mDNSResponder/dist/mDNSPosix/mDNSUNP.c | 20 +- .../dist/mDNSShared/PlatformCommon.c | 2 +- .../mDNSResponder/dist/mDNSShared/dns-sd.1 | 18 +- .../mDNSResponder/dist/mDNSShared/dns_sd.h | 37 +- .../dist/mDNSShared/dnssd_clientlib.c | 2 +- .../dist/mDNSShared/dnssd_clientstub.c | 75 +- .../mDNSResponder/dist/mDNSShared/dnssd_ipc.h | 9 +- .../dist/mDNSShared/uds_daemon.c | 1175 ++++++++-- .../dist/mDNSShared/uds_daemon.h | 1 + 19 files changed, 3412 insertions(+), 864 deletions(-) diff --git a/external/apache2/mDNSResponder/dist/Clients/dns-sd.c b/external/apache2/mDNSResponder/dist/Clients/dns-sd.c index c1c5d27f9a5d..cf3b6f19c934 100644 --- a/external/apache2/mDNSResponder/dist/Clients/dns-sd.c +++ b/external/apache2/mDNSResponder/dist/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, "EFBZLRPQqCAUNTMISV" + 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]; @@ -1117,40 +1131,44 @@ int main(int argc, char **argv) err = DNSServiceBrowse(&sc1, kDNSServiceFlagsShareConnection, opinterface, typ, dom, zonedata_browse, NULL); break; - case 'L': if (argc < opi+2) goto Fail; - typ = (argc < opi+2) ? "" : argv[opi+1]; - dom = (argc < opi+3) ? "local" : argv[opi+2]; - typ = gettype(buffer, typ); - if (dom[0] == '.' && dom[1] == 0) dom = "local"; // We allow '.' on the command line as a synonym for "local" - printf("Lookup %s.%s.%s\n", argv[opi+0], typ, dom); - err = DNSServiceResolve(&client, 0, opinterface, argv[opi+0], typ, dom, resolve_reply, NULL); - break; + case 'l': + case 'L': { + DNSServiceFlags rflags = 0; + if (argc < opi+2) goto Fail; + typ = (argc < opi+2) ? "" : argv[opi+1]; + dom = (argc < opi+3) ? "local" : argv[opi+2]; + typ = gettype(buffer, typ); + if (dom[0] == '.' && dom[1] == 0) dom = "local"; // We allow '.' on the command line as a synonym for "local" + printf("Lookup %s.%s.%s\n", argv[opi+0], typ, dom); + if (operation == 'l') rflags |= kDNSServiceFlagsWakeOnResolve; + err = DNSServiceResolve(&client, rflags, opinterface, argv[opi+0], typ, dom, resolve_reply, NULL); + break; + } case 'R': if (argc < opi+4) goto Fail; typ = (argc < opi+2) ? "" : argv[opi+1]; 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]); @@ -1186,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; } @@ -1318,7 +1336,7 @@ Fail: // NOT static -- otherwise the compiler may optimize it out // The "@(#) " pattern is a special prefix the "what" command looks for -const char VersionString_SCCS[] = "@(#) dns-sd " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")"; +const char VersionString_SCCS[] = "@(#) dns-sd " STRINGIFY(mDNSResponderVersion); #if _BUILDING_XCODE_PROJECT_ // If the process crashes, then this string will be magically included in the automatically-generated crash log diff --git a/external/apache2/mDNSResponder/dist/mDNSCore/DNSCommon.c b/external/apache2/mDNSResponder/dist/mDNSCore/DNSCommon.c index bb7a082a02d5..f7f2a77d285a 100644 --- a/external/apache2/mDNSResponder/dist/mDNSCore/DNSCommon.c +++ b/external/apache2/mDNSResponder/dist/mDNSCore/DNSCommon.c @@ -989,8 +989,31 @@ mDNSexport void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText) // Set up a AuthRecord with sensible default values. // These defaults may be overwritten with new values before mDNS_Register is called mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID, - mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, mDNSRecordCallback Callback, void *Context) + mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, AuthRecType artype, mDNSRecordCallback Callback, void *Context) { + // + // LocalOnly auth record can be created with LocalOnly InterfaceID or a valid InterfaceID. + // Most of the applications normally create with LocalOnly InterfaceID and we store them as + // such, so that we can deliver the response to questions that specify LocalOnly InterfaceID. + // LocalOnly resource records can also be created with valid InterfaceID which happens today + // when we create LocalOnly records for /etc/hosts. + + if (InterfaceID == mDNSInterface_LocalOnly && artype != AuthRecordLocalOnly) + { + LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch LocalOnly record InterfaceID %p called with artype %d", InterfaceID, artype); + return; + } + else if (InterfaceID == mDNSInterface_P2P && artype != AuthRecordP2P) + { + LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch P2P record InterfaceID %p called with artype %d", InterfaceID, artype); + return; + } + else if (!InterfaceID && (artype == AuthRecordP2P || artype == AuthRecordLocalOnly)) + { + LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch InterfaceAny record InterfaceID %p called with artype %d", InterfaceID, artype); + return; + } + // Don't try to store a TTL bigger than we can represent in platform time units if (ttl > 0x7FFFFFFFUL / mDNSPlatformOneSecond) ttl = 0x7FFFFFFFUL / mDNSPlatformOneSecond; @@ -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,6 +1098,12 @@ mDNSexport void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID I q->ForceMCast = mDNSfalse; q->ReturnIntermed = mDNSfalse; q->SuppressUnusable = 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; } @@ -1186,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); @@ -1205,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); @@ -1213,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. @@ -1226,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); @@ -1245,12 +1369,20 @@ mDNSexport mDNSBool AnyTypeRecordAnswersQuestion(const ResourceRecord *const rr, return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname)); } -// This is called only when the caller knows that it is a Unicast Resource Record and it is a Unicast Question -// and hence we don't need InterfaceID checks like above. Though this may not be a big optimization, the main -// reason we need this is that we can't compare DNSServers between the question and the resource record because -// the resource record may not be completely initialized e.g., mDNSCoreReceiveResponse -mDNSexport mDNSBool UnicastResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) +// This is called with both unicast resource record and multicast resource record. The question that +// received the unicast response could be the regular unicast response from a DNS server or a response +// to a mDNS QU query. The main reason we need this function is that we can't compare DNSServers between the +// question and the resource record because the resource record is not completely initialized in +// mDNSCoreReceiveResponse when this function is called. +mDNSexport mDNSBool ResourceRecordAnswersUnicastResponse(const ResourceRecord *const rr, const DNSQuestion *const q) { + // For resource records created using multicast, the InterfaceIDs have to match + if (rr->InterfaceID && + q->InterfaceID && rr->InterfaceID != q->InterfaceID) return(mDNSfalse); + + // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question. + 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. if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse); @@ -1746,7 +1878,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; @@ -1816,7 +1948,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); @@ -1831,7 +1963,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); @@ -1850,7 +1982,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; @@ -2156,7 +2288,7 @@ mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage case kDNSType_OPT: { rdataOPT *opt = rr->resrec.rdata->u.opt; rr->resrec.rdlength = 0; - while (ptr < end && (mDNSu8 *)(opt+1) < &rr->resrec.rdata->u.data[MaximumRDSize]) + while (ptr < end && (mDNSu8 *)(opt+1) < rr->resrec.rdata->u.data + MaximumRDSize) { const rdataOPT *const currentopt = opt; if (ptr + 4 > end) { LogInfo("GetLargeResourceRecord: OPT RDATA ptr + 4 > end"); goto fail; } @@ -2593,6 +2725,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); @@ -2618,12 +2751,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"); @@ -2640,9 +2774,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/external/apache2/mDNSResponder/dist/mDNSCore/DNSCommon.h b/external/apache2/mDNSResponder/dist/mDNSCore/DNSCommon.h index 401d57102d64..5df4ce410d93 100644 --- a/external/apache2/mDNSResponder/dist/mDNSCore/DNSCommon.h +++ b/external/apache2/mDNSResponder/dist/mDNSCore/DNSCommon.h @@ -164,7 +164,8 @@ extern mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBody *c extern mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q); 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 ResourceRecordAnswersUnicastResponse(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/external/apache2/mDNSResponder/dist/mDNSCore/DNSDigest.c b/external/apache2/mDNSResponder/dist/mDNSCore/DNSDigest.c index d3d0a5cdf7cf..b4a0158cc336 100644 --- a/external/apache2/mDNSResponder/dist/mDNSCore/DNSDigest.c +++ b/external/apache2/mDNSResponder/dist/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/external/apache2/mDNSResponder/dist/mDNSCore/mDNS.c b/external/apache2/mDNSResponder/dist/mDNSCore/mDNS.c index 5aeba5c51f14..305a1dae458c 100755 --- a/external/apache2/mDNSResponder/dist/mDNSCore/mDNS.c +++ b/external/apache2/mDNSResponder/dist/mDNSCore/mDNS.c @@ -75,6 +75,9 @@ 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); // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -109,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) @@ -129,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; @@ -159,7 +349,7 @@ mDNSexport mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterface if (addr->type == mDNSAddrType_IPv6) { - if (mDNSv6AddressIsLinkLocal(&addr->ip.v4)) return(mDNStrue); + if (mDNSv6AddressIsLinkLocal(&addr->ip.v6)) return(mDNStrue); for (intf = m->HostInterfaces; intf; intf = intf->next) if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx) if ((((intf->ip.ip.v6.l[0] ^ addr->ip.v6.l[0]) & intf->mask.ip.v6.l[0]) == 0) && @@ -185,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)) { @@ -203,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, @@ -228,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); + } // *************************************************************************** @@ -278,6 +579,9 @@ mDNSlocal void AnswerAllLocalQuestionsWithLocalAuthRecord(mDNS *const m, AuthRec #define GoodbyeCount ((mDNSu8)3) #define WakeupCount ((mDNSu8)18) +// Number of wakeups we send if WakeOnResolve is set in the question +#define InitialWakeOnResolveCount ((mDNSu8)3) + // Note that the announce intervals use exponential backoff, doubling each time. The probe intervals do not. // This means that because the announce interval is doubled after sending the first packet, the first // observed on-the-wire inter-packet interval between announcements is actually one second. @@ -517,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; @@ -644,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) { @@ -664,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; - while (*d && *d != rr) d=&(*d)->next; - if (*d || *p) + if (RRLocalOnly(rr)) { - LogMsg("Error! Tried to register AuthRecord %p %##s (%s) that's already in the list", - rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + 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) + { + 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); } @@ -693,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); @@ -701,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) @@ -776,6 +1177,11 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) rr->InFlightRDLen = 0; rr->QueuedRData = 0; rr->QueuedRDLen = 0; + //mDNSPlatformMemZero(&rr->NATinfo, sizeof(rr->NATinfo)); + // We should be recording the actual internal port for this service record here. Once we initiate our NAT mapping + // request we'll subsequently overwrite srv.port with the allocated external NAT port -- potentially multiple + // times with different values if the external NAT port changes during the lifetime of the service registration. + //if (rr->resrec.rrtype == kDNSType_SRV) rr->NATinfo.IntPort = rr->resrec.rdata->u.srv.port; // rr->resrec.interface = already set in mDNS_SetupResourceRecord // rr->resrec.name->c = MUST be set by client @@ -820,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)) { @@ -863,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)); @@ -884,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 @@ -938,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) { @@ -962,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; @@ -991,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)); } @@ -1084,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? @@ -1206,6 +1663,7 @@ mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const d AuthRecord *rr; AuthRecord *ResponseRecords = mDNSNULL; AuthRecord **nrp = &ResponseRecords; + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); // Make a list of all our records that need to be unicast to this destination for (rr = m->ResourceRecords; rr; rr=rr->next) @@ -1217,6 +1675,7 @@ mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const d rr->ImmedUnicast = mDNSfalse; if (rr->ImmedUnicast && rr->ImmedAnswer == InterfaceID) + { if ((dest->type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->v4Requester, dest->ip.v4)) || (dest->type == mDNSAddrType_IPv6 && mDNSSameIPv6Address(rr->v6Requester, dest->ip.v6))) { @@ -1224,9 +1683,18 @@ mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const d rr->ImmedUnicast = mDNSfalse; rr->v4Requester = zerov4Addr; rr->v6Requester = zerov6Addr; + + // Only sent records registered for P2P over P2P interfaces + if (intf && !mDNSPlatformValidRecordForInterface(rr, intf)) + { + LogInfo("SendDelayedUnicastResponse: Not sending %s, on %s", ARDisplayString(m, rr), InterfaceNameForID(m, InterfaceID)); + continue; + } + if (rr->NextResponse == mDNSNULL && nrp != &rr->NextResponse) // rr->NR_AnswerTo { rr->NR_AnswerTo = (mDNSu8*)~0; *nrp = rr; nrp = &rr->NextResponse; } } + } } AddAdditionalsToResponseList(m, ResponseRecords, &nrp, InterfaceID); @@ -1273,7 +1741,7 @@ mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const d } if (m->omsg.h.numAnswers) - mDNSSendDNSMessage(m, &m->omsg, responseptr, mDNSInterface_Any, mDNSNULL, dest, MulticastDNSPort, mDNSNULL, mDNSNULL); + mDNSSendDNSMessage(m, &m->omsg, responseptr, InterfaceID, mDNSNULL, dest, MulticastDNSPort, mDNSNULL, mDNSNULL); } } @@ -1629,6 +2097,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; } } @@ -1776,7 +2245,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; @@ -1883,7 +2361,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)); @@ -1920,7 +2398,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); @@ -1940,6 +2418,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); @@ -1972,7 +2451,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; } @@ -2273,6 +2752,58 @@ mDNSlocal int RecordDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDN return(i); } +mDNSlocal void mDNSSendWakeOnResolve(mDNS *const m, DNSQuestion *q) + { + int len, i, cnt; + mDNSInterfaceID InterfaceID = q->InterfaceID; + domainname *d = &q->qname; + + // We can't send magic packets without knowing which interface to send it on. + if (InterfaceID == mDNSInterface_Any || InterfaceID == mDNSInterface_LocalOnly || InterfaceID == mDNSInterface_P2P) + { + LogMsg("mDNSSendWakeOnResolve: ERROR!! Invalid InterfaceID %p for question %##s", InterfaceID, q->qname.c); + return; + } + + // Split MAC@IPAddress and pass them separately + len = d->c[0]; + i = 1; + cnt = 0; + for (i = 1; i < len; i++) + { + if (d->c[i] == '@') + { + char EthAddr[18]; // ethernet adddress : 12 bytes + 5 ":" + 1 NULL byte + char IPAddr[47]; // Max IP address len: 46 bytes (IPv6) + 1 NULL byte + if (cnt != 5) + { + LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed Ethernet address %##s, cnt %d", q->qname.c, cnt); + return; + } + if ((i - 1) > (int) (sizeof(EthAddr) - 1)) + { + LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed Ethernet address %##s, length %d", q->qname.c, i - 1); + return; + } + if ((len - i) > (int)(sizeof(IPAddr) - 1)) + { + LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed IP address %##s, length %d", q->qname.c, len - i); + return; + } + mDNSPlatformMemCopy(EthAddr, &d->c[1], i - 1); + EthAddr[i - 1] = 0; + mDNSPlatformMemCopy(IPAddr, &d->c[i + 1], len - i); + IPAddr[len - i] = 0; + mDNSPlatformSendWakeupPacket(m, InterfaceID, EthAddr, IPAddr, InitialWakeOnResolveCount - q->WakeOnResolveCount); + return; + } + else if (d->c[i] == ':') + cnt++; + } + LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed WakeOnResolve name %##s", q->qname.c); + } + + mDNSlocal mDNSBool AccelerateThisQuery(mDNS *const m, DNSQuestion *q) { // If more than 90% of the way to the query time, we should unconditionally accelerate it @@ -2568,7 +3099,14 @@ mDNSlocal void SendQueries(mDNS *const m) // If we're suppressing this question, or we successfully put it, update its SendQNow state if (SuppressOnThisInterface(q->DupSuppress, intf) || BuildQuestion(m, &m->omsg, &queryptr, q, &kalistptr, &answerforecast)) - q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf); + { + q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf); + if (q->WakeOnResolveCount) + { + mDNSSendWakeOnResolve(m, q); + q->WakeOnResolveCount--; + } + } } } @@ -2634,7 +3172,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); @@ -2683,7 +3221,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; } @@ -2765,12 +3303,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. @@ -2829,50 +3386,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 @@ -3266,25 +3780,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)); @@ -3325,35 +3829,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; @@ -3376,6 +3920,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 } @@ -3386,6 +3931,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)); @@ -3413,9 +3972,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) @@ -3428,16 +3989,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 + } } } @@ -3644,6 +4229,63 @@ mDNSlocal void CheckProxyRecords(mDNS *const m, AuthRecord *list) } } +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 + { + if (m->NextScheduledStopTime - q->StopTime > 0) + m->NextScheduledStopTime = q->StopTime; + } + } + // 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) { mDNS_Lock(m); // Must grab lock before trying to read m->timenow @@ -3652,6 +4294,8 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) { int i; AuthRecord *head, *tail; + mDNSu32 slot; + AuthGroup *ag; verbosedebugf("mDNS_Execute"); @@ -3671,7 +4315,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++) { @@ -3732,26 +4376,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"); @@ -3804,6 +4440,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); @@ -3852,6 +4513,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); @@ -3891,6 +4553,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 @@ -3931,7 +4614,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; @@ -3940,6 +4624,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; @@ -3981,6 +4774,9 @@ mDNSexport void mDNS_UpdateAllowSleep(mDNS *const m) { #ifndef IDLESLEEPCONTROL_DISABLED mDNSBool allowSleep = mDNStrue; + char reason[128]; + + reason[0] = 0; if (m->SystemSleepOnlyIfWakeOnLAN) { @@ -3988,6 +4784,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); } @@ -3997,12 +4794,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; } @@ -4011,22 +4809,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) */ } -#if 0 // Call the platform code to enable/disable sleep - mDNSPlatformSetAllowSleep(m, allowSleep); -#endif + 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; @@ -4046,7 +4843,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) { @@ -4059,40 +4857,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) @@ -4100,8 +4906,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])); @@ -4125,6 +4929,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) { @@ -4363,11 +5192,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., @@ -5465,6 +6289,8 @@ mDNSlocal DNSQuestion *ExpectingUnicastResponseForQuestion(const mDNS *const m, return(mDNSNULL); } +// This function is called when we receive a unicast response. This could be the case of a unicast response from the +// DNS server or a response to the QU query. Hence, the cache record's InterfaceId can be both NULL or non-NULL (QU case) mDNSlocal DNSQuestion *ExpectingUnicastResponseForRecord(mDNS *const m, const mDNSAddr *const srcaddr, const mDNSBool SrcLocal, const mDNSIPPort port, const mDNSOpaque16 id, const CacheRecord *const rr, mDNSBool tcp) { @@ -5472,12 +6298,9 @@ mDNSlocal DNSQuestion *ExpectingUnicastResponseForRecord(mDNS *const m, (void)id; (void)srcaddr; - // Unicast records have zero as InterfaceID - if (rr->resrec.InterfaceID) return mDNSNULL; - for (q = m->Questions; q; q=q->next) { - if (!q->DuplicateOf && UnicastResourceRecordAnswersQuestion(&rr->resrec, q)) + if (!q->DuplicateOf && ResourceRecordAnswersUnicastResponse(&rr->resrec, q)) { if (!mDNSOpaque16IsZero(q->TargetQID)) { @@ -5917,7 +6740,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 @@ -5931,11 +6754,14 @@ 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 same machine giving - // different answers for the reverse mapping record.) This is simply a misconfiguration, and we don't try to recover from it. + // We assumed this record must be unique, but we were wrong. (e.g. There are two mDNSResponders on the + // same machine giving different answers for the reverse mapping record, or there are two machines on the + // network using the same IP address.) This is simply a misconfiguration, and there's nothing we can do + // to fix it -- e.g. it's not our job to be trying to change the machine's IP address. We just discard our + // record to avoid continued conflicts (as we do for a conflict on our Unique records) and get on with life. else if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique) { LogMsg("mDNSCoreReceiveResponse: Unexpected conflict discarding %s", ARDisplayString(m, rr)); @@ -6227,6 +7053,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 @@ -6237,22 +7074,25 @@ exit: // *not* on a Microsoft Active Directory network, and there is no authoritative server for "local". Note that this is not // 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 :-) + // + // By suppressing negative responses, it might take longer to timeout a .local question as it might be expecting a + // response e.g., we deliver a positive "A" response and suppress negative "AAAA" response and the upper layer may + // be waiting longer to get the AAAA response before returning the "A" response to the application. To handle this + // case without creating the negative cache entries, we generate a negative response and let the layer above us + // do the appropriate thing. This negative response is also needed for appending new search domains. 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 (!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 @@ -6342,16 +7182,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); } @@ -6363,11 +7216,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); @@ -6466,7 +7325,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; @@ -6509,7 +7368,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); @@ -6526,7 +7385,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) @@ -6546,15 +7404,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. @@ -6761,10 +7634,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; @@ -6818,6 +7706,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) @@ -6871,14 +7804,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) @@ -6888,6 +7849,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)) { @@ -6901,16 +7873,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 @@ -7100,16 +8078,20 @@ mDNSlocal mDNSBool ShouldSuppressQuery(mDNS *const m, domainname *qname, mDNSu16 { LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Local Address %.4a found", qname, DNSTypeName(qtype), &i->ip.ip.v4); + if (m->SleepState == SleepState_Sleeping) + LogInfo("ShouldSuppressQuery: Would have returned true earlier"); return mDNSfalse; } else if (iptype == mDNSAddrType_IPv6 && !mDNSv6AddressIsLoopback(&i->ip.ip.v6) && !mDNSv6AddressIsLinkLocal(&i->ip.ip.v6) && !mDNSSameIPv6Address(i->ip.ip.v6, m->AutoTunnelHostAddr) && - !mDNSSameIPv6Address(i->ip.ip.v6, m->AutoTunnelRelayAddr)) + !mDNSSameIPv6Address(i->ip.ip.v6, m->AutoTunnelRelayAddrOut)) { LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Local Address %.16a found", qname, DNSTypeName(qtype), &i->ip.ip.v6); + if (m->SleepState == SleepState_Sleeping) + LogInfo("ShouldSuppressQuery: Would have returned true earlier"); return mDNSfalse; } } @@ -7118,15 +8100,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) @@ -7134,15 +8113,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--; @@ -7157,11 +8136,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); @@ -7172,7 +8151,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) @@ -7183,10 +8161,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, @@ -7194,38 +8234,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) + // + // 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) { - qnext = q->next; + 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)); + // 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 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)); } - } + 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. // @@ -7240,8 +8287,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. // @@ -7275,8 +8322,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; } @@ -7342,6 +8389,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() @@ -7379,7 +8427,15 @@ 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; + mDNS_PurgeBeforeResolve(m, question); + } + else + question->WakeOnResolveCount = 0; if (question->DuplicateOf) question->AuthInfo = question->DuplicateOf->AuthInfo; @@ -7415,21 +8471,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)); } @@ -7452,6 +8515,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); } @@ -7543,6 +8612,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; @@ -7684,6 +8760,12 @@ mDNSlocal mStatus mDNS_StartBrowse_internal(mDNS *const m, DNSQuestion *const qu question->ForceMCast = ForceMCast; question->ReturnIntermed = mDNSfalse; question->SuppressUnusable = 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); @@ -7857,6 +8939,12 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, query->qSRV.ForceMCast = mDNSfalse; query->qSRV.ReturnIntermed = mDNSfalse; query->qSRV.SuppressUnusable = 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; @@ -7871,6 +8959,12 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, query->qTXT.ForceMCast = mDNSfalse; query->qTXT.ReturnIntermed = mDNSfalse; query->qTXT.SuppressUnusable = 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; @@ -7885,6 +8979,12 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, query->qAv4.ForceMCast = mDNSfalse; query->qAv4.ReturnIntermed = mDNSfalse; query->qAv4.SuppressUnusable = 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; @@ -7899,6 +8999,12 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, query->qAv6.ForceMCast = mDNSfalse; query->qAv6.ReturnIntermed = mDNSfalse; query->qAv6.SuppressUnusable = 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; @@ -7949,6 +9055,12 @@ mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, m question->ForceMCast = mDNSfalse; question->ReturnIntermed = mDNSfalse; question->SuppressUnusable = 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); @@ -8001,7 +9113,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 @@ -8011,8 +9123,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 { @@ -8068,9 +9180,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; @@ -8322,10 +9434,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); @@ -8341,7 +9453,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); } @@ -8375,7 +9487,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. @@ -8400,13 +9512,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, @@ -8457,12 +9576,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); @@ -8663,10 +9777,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; @@ -8676,16 +9793,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 @@ -8720,7 +9852,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; @@ -8753,6 +9885,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 @@ -8770,14 +9910,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); @@ -8849,7 +10000,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 @@ -8858,7 +10009,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); @@ -8935,9 +10086,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; @@ -8950,7 +10112,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)); @@ -9373,7 +10543,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"); } @@ -9492,6 +10662,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; @@ -9499,7 +10670,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; @@ -9510,6 +10680,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; @@ -9523,6 +10694,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; @@ -9533,6 +10708,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; @@ -9559,7 +10735,7 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->AutoTunnelHostAddrActive = mDNSfalse; m->AutoTunnelLabel.c[0] = 0; - m->RegisterSearchDomains = mDNSfalse; + m->StartWABQueries = mDNSfalse; m->RegisterAutoTunnel6 = mDNStrue; // NAT traversal fields @@ -9675,6 +10851,46 @@ mDNSlocal void PurgeOrReconfirmCacheRecord(mDNS *const m, CacheRecord *cr, const } } +mDNSlocal void mDNS_PurgeBeforeResolve(mDNS *const m, DNSQuestion *q) + { + const mDNSu32 slot = HashSlot(&q->qname); + CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + CacheRecord *rp; + + for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next) + { + if (SameNameRecordAnswersQuestion(&rp->resrec, q)) + { + LogInfo("mDNS_PurgeBeforeResolve: Flushing %s", CRDisplayString(m, rp)); + mDNS_PurgeCacheResourceRecord(m, rp); + } + } + } + +// Check for a positive unicast response to the question but with qtype +mDNSexport mDNSBool mDNS_CheckForCacheRecord(mDNS *const m, DNSQuestion *q, mDNSu16 qtype) + { + DNSQuestion question; + const mDNSu32 slot = HashSlot(&q->qname); + CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); + CacheRecord *rp; + + // Create an identical question but with qtype + mDNS_SetupQuestion(&question, q->InterfaceID, &q->qname, qtype, mDNSNULL, mDNSNULL); + question.qDNSServer = q->qDNSServer; + + for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next) + { + if (!rp->resrec.InterfaceID && rp->resrec.RecordType != kDNSRecordTypePacketNegative && + SameNameRecordAnswersQuestion(&rp->resrec, &question)) + { + LogInfo("mDNS_CheckForCacheRecord: Found %s", CRDisplayString(m, rp)); + return mDNStrue; + } + } + return mDNSfalse; + } + mDNSlocal void CacheRecordResetDNSServer(mDNS *const m, DNSQuestion *q, DNSServer *new) { const mDNSu32 slot = HashSlot(&q->qname); @@ -9851,24 +11067,53 @@ 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"); - if (m->RegisterSearchDomains) uDNS_RegisterSearchDomains(m); + // Let the platform layer get the current DNS 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 : 0); mDNS_Lock(m); - // Let the platform layer get the current DNS information - // The m->RegisterSearchDomains boolean is so that we lazily get the search domain list only on-demand - // (no need to hit the network with domain enumeration queries until we actually need that information). for (ptr = m->DNSServers; ptr; ptr = ptr->next) { ptr->penaltyTime = 0; 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 @@ -9894,7 +11139,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/external/apache2/mDNSResponder/dist/mDNSCore/mDNSEmbeddedAPI.h b/external/apache2/mDNSResponder/dist/mDNSCore/mDNSEmbeddedAPI.h index 509d50b9f499..ee430afadceb 100755 --- a/external/apache2/mDNSResponder/dist/mDNSCore/mDNSEmbeddedAPI.h +++ b/external/apache2/mDNSResponder/dist/mDNSCore/mDNSEmbeddedAPI.h @@ -112,7 +112,10 @@ // In the event that structures are not packed correctly, mDNS_Init() will detect this and report an error, so the // developer will know what's wrong, and can investigate what needs to be done on that compiler to provide proper packing. #ifndef packedstruct - #if ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 9))) + #ifdef __packed + #define packedstruct struct __packed + #define packedunion union __packed + #elif ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 9))) #define packedstruct struct __attribute__((__packed__)) #define packedunion union __attribute__((__packed__)) #else @@ -348,6 +351,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 +410,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 +435,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 +1048,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 +1077,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 +1136,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 +1201,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 +1286,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 +1319,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 @@ -1391,25 +1469,29 @@ enum { NoAnswer_Normal = 0, NoAnswer_Suspended = 1, NoAnswer_Fail = 2 }; #define MD5_LEN 16 #define AutoTunnelUnregistered(X) ( \ - (X)->AutoTunnelHostRecord.resrec.RecordType == kDNSRecordTypeUnregistered && \ - (X)->AutoTunnelDeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered && \ - (X)->AutoTunnelService. resrec.RecordType == kDNSRecordTypeUnregistered && \ - (X)->AutoTunnel6Record. resrec.RecordType == kDNSRecordTypeUnregistered ) + (X)->AutoTunnelHostRecord. resrec.RecordType == kDNSRecordTypeUnregistered && \ + (X)->AutoTunnelDeviceInfo. resrec.RecordType == kDNSRecordTypeUnregistered && \ + (X)->AutoTunnelService. resrec.RecordType == kDNSRecordTypeUnregistered && \ + (X)->AutoTunnel6Record. resrec.RecordType == kDNSRecordTypeUnregistered && \ + (X)->AutoTunnel6MetaRecord.resrec.RecordType == kDNSRecordTypeUnregistered ) // Internal data structure to maintain authentication information 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 AuthRecord AutoTunnelService; // Service record (possibly NAT-Mapped) of IKE daemon implementing tunnel endpoint - AuthRecord AutoTunnel6Record; // AutoTunnel AAAA Record obtained from Connectivityd + AuthRecord AutoTunnel6Record; // AutoTunnel AAAA record obtained from awacsd + AuthRecord AutoTunnel6MetaRecord; // Notify remote peers to connect to the relay servers for potential outbound connections from this host 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,6 +1534,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; @@ -1460,7 +1546,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 @@ -1493,6 +1579,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 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; }; @@ -1555,6 +1648,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 { @@ -1573,6 +1667,7 @@ typedef struct DNameListElem typedef struct ClientTunnel { struct ClientTunnel *next; + const char *prefix; domainname dstname; mDNSBool MarkedForDeletion; mDNSv6Addr loc_inner; @@ -1634,20 +1729,24 @@ 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 + 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 DirQ; - int numDirAnswers; + int numCfAnswers; ARListElem *AuthRecs; } SearchListElem; @@ -1670,9 +1769,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 @@ -1741,12 +1839,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 @@ -1755,6 +1856,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 @@ -1764,8 +1867,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; @@ -1776,6 +1880,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 @@ -1790,10 +1895,15 @@ struct mDNS_struct HostnameInfo *Hostnames; // List of registered hostnames + hostname metadata mDNSv6Addr AutoTunnelHostAddr; // IPv6 address advertised for AutoTunnel services on this machine mDNSBool AutoTunnelHostAddrActive; - mDNSv6Addr AutoTunnelRelayAddr; // IPv6 address advertised for AutoTunnel Relay services on this machine + // AutoTunnel Relay address has two distinct uses + // AutoTunnelRelayAddrIn: If non-zero, it means that this host can be reached (inbound connection) through the relay + // AutoTunnelRelayAddrOut: If non-zero, it means that this host can use the relay to reach (outbound connection) the + // other hosts through the relay + mDNSv6Addr AutoTunnelRelayAddrIn; + mDNSv6Addr AutoTunnelRelayAddrOut; domainlabel AutoTunnelLabel; // Used to construct hostname for *IPv4* address of tunnel endpoints - mDNSBool RegisterSearchDomains; + mDNSBool StartWABQueries; // Start WAB queries for the purpose of domain enumeration mDNSBool RegisterAutoTunnel6; // NAT-Traversal fields @@ -2027,6 +2137,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) @@ -2098,14 +2209,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); @@ -2114,7 +2232,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, @@ -2152,7 +2270,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 @@ -2314,9 +2432,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 - @@ -2330,10 +2445,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); @@ -2357,13 +2473,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 @@ -2478,7 +2596,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. @@ -2531,7 +2649,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. @@ -2587,7 +2707,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); @@ -2606,9 +2730,19 @@ 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); +extern mDNSBool mDNS_CheckForCacheRecord(mDNS *const m, DNSQuestion *q, mDNSu16 qtype); // 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 @@ -2619,7 +2753,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 @@ -2741,6 +2874,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) @@ -2801,17 +2945,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) <= 752) ? 1 : -1]; - char sizecheck_ZoneData [(sizeof(ZoneData) <= 1588) ? 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) <= 3050) ? 1 : -1]; + char sizecheck_DomainAuthInfo [(sizeof(DomainAuthInfo) <= 7968) ? 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/external/apache2/mDNSResponder/dist/mDNSCore/uDNS.c b/external/apache2/mDNSResponder/dist/mDNSCore/uDNS.c index dcfcf6583fdd..5c4e3a174846 100755 --- a/external/apache2/mDNSResponder/dist/mDNSCore/uDNS.c +++ b/external/apache2/mDNSResponder/dist/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) @@ -372,12 +384,13 @@ mDNSexport mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info, // Caution: Only zero AutoTunnelHostRecord.namestorage and AutoTunnelNAT.clientContext AFTER we've determined that this is a NEW DomainAuthInfo // being added to the list. Otherwise we risk smashing our AutoTunnel host records and NATOperation that are already active and in use. - info->AutoTunnelHostRecord.resrec.RecordType = kDNSRecordTypeUnregistered; - info->AutoTunnelHostRecord.namestorage.c[0] = 0; - info->AutoTunnelTarget .resrec.RecordType = kDNSRecordTypeUnregistered; - info->AutoTunnelDeviceInfo.resrec.RecordType = kDNSRecordTypeUnregistered; - info->AutoTunnelService .resrec.RecordType = kDNSRecordTypeUnregistered; - info->AutoTunnel6Record .resrec.RecordType = kDNSRecordTypeUnregistered; + info->AutoTunnelHostRecord .resrec.RecordType = kDNSRecordTypeUnregistered; + info->AutoTunnelHostRecord .namestorage.c[0] = 0; + info->AutoTunnelTarget .resrec.RecordType = kDNSRecordTypeUnregistered; + info->AutoTunnelDeviceInfo .resrec.RecordType = kDNSRecordTypeUnregistered; + info->AutoTunnelService .resrec.RecordType = kDNSRecordTypeUnregistered; + info->AutoTunnel6Record .resrec.RecordType = kDNSRecordTypeUnregistered; + info->AutoTunnel6MetaRecord.resrec.RecordType = kDNSRecordTypeUnregistered; info->AutoTunnelNAT.clientContext = mDNSNULL; info->next = mDNSNULL; *p = info; @@ -553,6 +566,9 @@ mDNSexport mStatus mDNS_StartNATOperation_internal(mDNS *const m, NATTraversalIn { LogMsg("Error! Tried to add a NAT traversal that's already in the active list: request %p Prot %d Int %d TTL %d", traversal, traversal->Protocol, mDNSVal16(traversal->IntPort), traversal->NATLease); + #if ForceAlerts + *(long*)0 = 0; + #endif return(mStatus_AlreadyRegistered); } if (traversal->Protocol && traversal->Protocol == (*n)->Protocol && mDNSSameIPPort(traversal->IntPort, (*n)->IntPort) && @@ -698,7 +714,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); @@ -1572,6 +1588,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.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; @@ -1600,15 +1622,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 @@ -1664,7 +1717,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 @@ -1709,7 +1762,7 @@ mDNSlocal void CompleteRecordNatMap(mDNS *m, NATTraversalInfo *n) if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4)) { LogInfo("CompleteRecordNatMap called for %s but no zone information!", ARDisplayString(m, rr)); - // We need to clear out the NATinfo state so that it will result in re-acuqiring the mapping + // We need to clear out the NATinfo state so that it will result in re-acquiring the mapping // and hence this callback called again. if (rr->NATinfo.clientContext) { @@ -1802,8 +1855,13 @@ mDNSlocal void StartRecordNatMap(mDNS *m, AuthRecord *rr) else if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_udp")) protocol = NATOp_MapUDP; else { LogMsg("StartRecordNatMap: could not determine transport protocol of service %##s", rr->resrec.name->c); return; } + //LogMsg("StartRecordNatMap: clientContext %p IntPort %d srv.port %d %s", + // rr->NATinfo.clientContext, mDNSVal16(rr->NATinfo.IntPort), mDNSVal16(rr->resrec.rdata->u.srv.port), ARDisplayString(m, rr)); if (rr->NATinfo.clientContext) mDNS_StopNATOperation_internal(m, &rr->NATinfo); rr->NATinfo.Protocol = protocol; + + // Shouldn't be trying to set IntPort here -- + // BuildUpdateMessage overwrites srs->RR_SRV.resrec.rdata->u.srv.port with external (mapped) port number rr->NATinfo.IntPort = rr->resrec.rdata->u.srv.port; rr->NATinfo.RequestedPort = rr->resrec.rdata->u.srv.port; rr->NATinfo.NATLease = 0; // Request default lease @@ -1818,6 +1876,19 @@ mDNSlocal void StartRecordNatMap(mDNS *m, AuthRecord *rr) // record is temporarily left in the ResourceRecords list so that we can initialize later // when the target is resolvable. Similarly, when host name changes, we enter regState_NoTarget // and we do the same. + +// This UnlinkResourceRecord routine is very worrying. It bypasses all the normal cleanup performed +// by mDNS_Deregister_internal and just unceremoniously cuts the record from the active list. +// This is why re-regsitering this record was producing syslog messages like this: +// "Error! Tried to add a NAT traversal that's already in the active list" +// Right now UnlinkResourceRecord is fortunately only called by RegisterAllServiceRecords, +// which then immediately calls mDNS_Register_internal to re-register the record, which probably +// masked more serious problems. Any other use of UnlinkResourceRecord is likely to lead to crashes. +// For now we'll workaround that specific problem by explicitly calling mDNS_StopNATOperation_internal, +// but long-term we should either stop cancelling the record registration and then re-registering it, +// or if we really do need to do this for some reason it should be done via the usual +// mDNS_Deregister_internal path instead of just cutting the record from the list. + mDNSlocal mStatus UnlinkResourceRecord(mDNS *const m, AuthRecord *const rr) { AuthRecord **list = &m->ResourceRecords; @@ -1826,6 +1897,15 @@ mDNSlocal mStatus UnlinkResourceRecord(mDNS *const m, AuthRecord *const rr) { *list = rr->next; rr->next = mDNSNULL; + + // Temporary workaround to cancel any active NAT mapping operation + if (rr->NATinfo.clientContext) + { + mDNS_StopNATOperation_internal(m, &rr->NATinfo); + rr->NATinfo.clientContext = mDNSNULL; + if (rr->resrec.rrtype == kDNSType_SRV) rr->resrec.rdata->u.srv.port = rr->NATinfo.IntPort; + } + return(mStatus_NoError); } LogMsg("UnlinkResourceRecord:ERROR!! - no such active record %##s", rr->resrec.name->c); @@ -2022,7 +2102,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; @@ -2048,7 +2128,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; @@ -2189,6 +2269,12 @@ mDNSlocal void GetStaticHostname(mDNS *m) q->ForceMCast = mDNSfalse; q->ReturnIntermed = mDNStrue; q->SuppressUnusable = 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; @@ -2927,7 +3013,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, @@ -3071,7 +3157,7 @@ mDNSlocal void hndlRecordUpdateReply(mDNS *m, AuthRecord *rr, mStatus err, mDNSu rr->updateError = err; #if APPLE_OSX_mDNSResponder - if (err == mStatus_BadSig) UpdateAutoTunnelDomainStatuses(m); + if (err == mStatus_BadSig || err == mStatus_BadKey) UpdateAutoTunnelDomainStatuses(m); #endif SetRecordRetry(m, rr, random); @@ -4030,7 +4116,6 @@ mDNSlocal const mDNSu8 *mDNS_WABLabels[] = (const mDNSu8 *)"\002lb", (const mDNSu8 *)"\001r", (const mDNSu8 *)"\002dr", - (const mDNSu8 *)"\002cf", (const mDNSu8 *)mDNSNULL, }; @@ -4569,28 +4654,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, change it to "leave alone" - if ((*p)->flag == -1) (*p)->flag = 0; + // 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 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)->flag = 1; // add - (*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) @@ -4599,95 +4697,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 FoundDirDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) - { - SearchListElem *slElem = question->QuestionContext; - mDNSBool RegisterAutoTunnel6 = mDNStrue; - char *res = "DisableInboundRelay"; - - LogInfo("FoundDirDomain: InterfaceID %p %s Question %##s Answer %s", answer->InterfaceID, AddRecord ? "Add" : "Rmv", question->qname.c, RRDisplayString(m, answer)); - if (answer->rrtype != kDNSType_TXT) - { - LogMsg("FoundDirDomain: answer type is not TXT %s for question %##s", DNSTypeName(answer->rrtype), question->qname.c); - return; - } - if (answer->RecordType == kDNSRecordTypePacketNegative) - { - LogInfo("FoundDirDomain: Negative answer for %##s", question->qname.c); - return; - } - if (answer->InterfaceID == mDNSInterface_LocalOnly) - { - LogInfo("FoundDirDomain: LocalOnly interfaceID for %##s", question->qname.c); - return; - } - - // TXT record is encoded as - if (answer->rdlength != mDNSPlatformStrLen(res) + 1) - { - LogInfo("FoundDirDomain: 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("FoundDirDomain: 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->numDirAnswers++; - RegisterAutoTunnel6 = mDNSfalse; - } - else - { - const SearchListElem *s; - slElem->numDirAnswers--; - if (slElem->numDirAnswers < 0) LogMsg("FoundDirDomain: numDirAnswers less than zero %d", slElem->numDirAnswers); - // See if any domain (including the slElem) has any answers - for (s=SearchList; s; s=s->next) - if (s->numDirAnswers) { 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; @@ -4711,7 +4720,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); @@ -4758,7 +4767,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; @@ -4773,68 +4782,50 @@ mDNSexport void udns_validatelists(void *const v) } #endif -mDNSlocal void mDNS_StartDirQuestion(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->QuestionCallback = FoundDirDomain; - question->QuestionContext = context; - LogInfo("mDNS_StartDirQuestion: Start DIR domain question %##s", question->qname.c); - if (mDNS_StartQuery(m, question)) - LogMsg("mDNS_StartDirQuestion: ERROR!! cannot start _dir._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_RegisterSearchDomains(mDNS *const m) + +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 (-1) - for (ptr = SearchList; ptr; ptr = ptr->next) ptr->flag = -1; + // 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); - m->RegisterSearchDomains = mDNStrue; - mDNSPlatformSetDNSConfig(m, mDNSfalse, m->RegisterSearchDomains, mDNSNULL, mDNSNULL, mDNSNULL); + mDNSPlatformSetDNSConfig(m, mDNSfalse, mDNStrue, mDNSNULL, mDNSNULL, mDNSNULL); mDNS_Unlock(m); + if (action & UDNS_START_WAB_QUERY) + m->StartWABQueries = mDNStrue; + // delete elems marked for removal, do queries for elems marked add while (*p) { ptr = *p; - LogInfo("RegisterSearchDomains %d %p %##s", ptr->flag, ptr->AuthRecs, ptr->domain.c); - if (ptr->flag == -1) // remove + 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; ptr->AuthRecs = mDNSNULL; *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 FoundDirDomain) - // 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 (!SameDomainName(&ptr->domain, &localdomain)) + // We suppressed the domain enumeration for scoped search domains below. When we enable that + // enable this. + if ((ptr->flag & SLE_WAB_QUERY_STARTED) && + !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any)) { mDNS_StopGetDomains(m, &ptr->BrowseQ); mDNS_StopGetDomains(m, &ptr->RegisterQ); mDNS_StopGetDomains(m, &ptr->DefBrowseQ); mDNS_StopGetDomains(m, &ptr->DefRegisterQ); mDNS_StopGetDomains(m, &ptr->AutomaticBrowseQ); - mDNS_StopGetDomains(m, &ptr->DirQ); } + mDNSPlatformMemFree(ptr); // deregister records generated from answers to the query @@ -4844,49 +4835,121 @@ mDNSexport mStatus uDNS_RegisterSearchDomains(mDNS *const m) arList = arList->next; debugf("Deregistering PTR %##s -> %##s", dereg->ar.resrec.name->c, dereg->ar.resrec.rdata->u.name.c); err = mDNS_Deregister(m, &dereg->ar); - if (err) LogMsg("uDNS_RegisterSearchDomains ERROR!! mDNS_Deregister returned %d", err); + if (err) LogMsg("uDNS_SetupSearchDomains:: ERROR!! mDNS_Deregister returned %d", err); // Memory will be freed in the FreeARElemCallback } continue; } - if (ptr->flag == 1) // add + 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)) + // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries. + // 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_RegisterSearchDomains: GetDomains for domain %##s returned error(s):\n" + LogMsg("uDNS_SetupSearchDomains: GetDomains for domain %##s returned error(s):\n" "%d (mDNS_DomainTypeBrowse)\n" "%d (mDNS_DomainTypeBrowseDefault)\n" "%d (mDNS_DomainTypeRegistration)\n" "%d (mDNS_DomainTypeRegistrationDefault)" "%d (mDNS_DomainTypeBrowseAutomatic)\n", ptr->domain.c, err1, err2, err3, err4, err5); - mDNS_StartDirQuestion(m, &ptr->DirQ, &ptr->domain, ptr); + ptr->flag |= SLE_WAB_QUERY_STARTED; } - ptr->flag = 0; } - if (ptr->flag) { LogMsg("uDNS_RegisterSearchDomains - unknown flag %d. Skipping.", ptr->flag); } - p = &ptr->next; } - // if there is any domain has answers, need to deregister autotunnel6 - for (s=SearchList; s; s=s->next) - if (s->numDirAnswers) { RegisterAutoTunnel6 = mDNSfalse; break; } -#if APPLE_OSX_mDNSResponder - CheckAutoTunnel6Registration(m, RegisterAutoTunnel6); -#endif 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) + { + 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")) + { + LogInfo("uDNS_GetNextSearchDomain: skipping local domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID); + (*searchIndex)++; + p = p->next; + continue; + } + } + // 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; + } + +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); + } + } + } + +// 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: // 1) query for b._dns-sd._udp.local on LocalOnly interface // (.local manually generated via explicit callback) @@ -4903,5 +4966,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) <= 4800) ? 1 : -1]; + char sizecheck_SearchListElem[(sizeof(SearchListElem) <= 5000) ? 1 : -1]; }; diff --git a/external/apache2/mDNSResponder/dist/mDNSCore/uDNS.h b/external/apache2/mDNSResponder/dist/mDNSCore/uDNS.h index 0562dae17b70..2dfaf51ec8c9 100755 --- a/external/apache2/mDNSResponder/dist/mDNSCore/uDNS.h +++ b/external/apache2/mDNSResponder/dist/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); @@ -91,7 +96,16 @@ extern void UpdateAllSRVRecords(mDNS *m); extern void CheckNATMappings(mDNS *m); extern mStatus uDNS_SetupDNSConfig(mDNS *const m); -extern mStatus uDNS_RegisterSearchDomains(mDNS *const m); + +// uDNS_SetupSearchDomains by default adds search domains. It also can be called with one or +// more values for "action" which does the following: +// +// -UDNS_START_WAB_QUERY - start Wide Area Bonjour (domain enumeration) queries + +#define UDNS_START_WAB_QUERY 0x00000001 + +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/external/apache2/mDNSResponder/dist/mDNSPosix/PosixDaemon.c b/external/apache2/mDNSResponder/dist/mDNSPosix/PosixDaemon.c index 76681eb6fd05..350063161f8d 100644 --- a/external/apache2/mDNSResponder/dist/mDNSPosix/PosixDaemon.c +++ b/external/apache2/mDNSResponder/dist/mDNSPosix/PosixDaemon.c @@ -47,8 +47,13 @@ extern int daemon(int, int); #include "mDNSPosix.h" #include "mDNSUNP.h" // For daemon() #include "uds_daemon.h" +#include "DNSCommon.h" #include "PlatformCommon.h" +#ifndef MDNSD_USER +#define MDNSD_USER "nobody" +#endif + #define CONFIG_FILE "/etc/mdnsd.conf" static domainname DynDNSZone; // Default wide-area zone for service registration static domainname DynDNSHostname; @@ -89,8 +94,10 @@ static void Reconfigure(mDNS *m) mDNSAddr DynDNSIP; const mDNSAddr dummy = { mDNSAddrType_IPv4, { { { 1, 1, 1, 1 } } } };; mDNS_SetPrimaryInterfaceInfo(m, NULL, NULL, NULL); + mDNS_Lock(m); if (ParseDNSServers(m, uDNS_SERVERS_FILE) < 0) LogMsg("Unable to parse DNS server list. Unicast DNS-SD unavailable"); + mDNS_Unlock(m); ReadDDNSSettingsFromConfFile(m, CONFIG_FILE, &DynDNSHostname, &DynDNSZone, NULL); mDNSPlatformSourceAddrForDest(&DynDNSIP, &dummy); if (DynDNSHostname.c[0]) mDNS_AddDynDNSHostName(m, &DynDNSHostname, NULL, NULL); @@ -121,8 +128,40 @@ mDNSlocal void ParseCmdLinArgs(int argc, char **argv) mDNSlocal void DumpStateLog(mDNS *const m) // Dump a little log of what we've been up to. { + DNSServer *s; + PosixNetworkInterface *i; + LogMsg("---- BEGIN STATE LOG ----"); udsserver_info(m); + + LogMsgNoIdent("----- Network Interfaces -------"); + for (i = (PosixNetworkInterface*)(m->HostInterfaces); + i; i = (PosixNetworkInterface *)(i->coreIntf.next)) { + LogMsg("%p %p %d %s%s%s%s%s %-8s %#a", i, + (void *)(i->coreIntf.InterfaceID), i->index, + i->coreIntf.InterfaceActive ? "-" : "D", + i->coreIntf.IPv4Available ? "4" : "-", + i->coreIntf.IPv6Available ? "6" : "-", + i->coreIntf.Advertise ? "A" : "-", + i->coreIntf.McastTxRx ? "M" : "-", + i->intfName, &(i->coreIntf.ip)); + } + + LogMsgNoIdent("--------- DNS Servers ----------"); + if (!mDNSStorage.DNSServers) LogMsgNoIdent(""); + else + { + for (s = m->DNSServers; s; s = s->next) + { + LogMsgNoIdent("DNS Server %##s %#a:%d %s", + s->domain.c, &s->addr, mDNSVal16(s->port), + s->teststate == DNSServer_Untested ? "(Untested)" : + s->teststate == DNSServer_Passed ? "" : + s->teststate == DNSServer_Failed ? "(Failed)" : + s->teststate == DNSServer_Disabled ? "(Disabled)" : "(Unknown state)"); + } + } + LogMsg("---- END STATE LOG ----"); } @@ -134,6 +173,10 @@ mDNSlocal mStatus MainLoop(mDNS *m) // Loop until we quit. mDNSPosixListenForSignalInEventLoop(SIGINT); mDNSPosixListenForSignalInEventLoop(SIGTERM); mDNSPosixListenForSignalInEventLoop(SIGUSR1); +#ifdef HAVE_SIGINFO + mDNSPosixListenForSignalInEventLoop(SIGUSR2); + mDNSPosixListenForSignalInEventLoop(SIGINFO); +#endif mDNSPosixListenForSignalInEventLoop(SIGPIPE); mDNSPosixListenForSignalInEventLoop(SIGHUP) ; @@ -160,7 +203,22 @@ mDNSlocal mStatus MainLoop(mDNS *m) // Loop until we quit. (void) mDNSPosixRunEventLoopOnce(m, &timeout, &signals, &gotData); if (sigismember(&signals, SIGHUP )) Reconfigure(m); +#ifdef HAVE_SIGINFO + /* use OSX-compatible signals since we can, and gain enhanced debugging */ + if (sigismember(&signals, SIGINFO)) DumpStateLog(m); + if (sigismember(&signals, SIGUSR1)) + { + mDNS_LoggingEnabled = mDNS_LoggingEnabled ? 0 : 1; + LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled ? "Enabled" : "Disabled"); + } + if (sigismember(&signals, SIGUSR2)) + { + mDNS_PacketLoggingEnabled = mDNS_PacketLoggingEnabled ? 0 : 1; + LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled"); + } +#else if (sigismember(&signals, SIGUSR1)) DumpStateLog(m); +#endif // SIGPIPE happens when we try to write to a dead client; death should be detected soon in request_callback() and cleaned up. if (sigismember(&signals, SIGPIPE)) LogMsg("Received SIGPIPE - ignoring"); if (sigismember(&signals, SIGINT) || sigismember(&signals, SIGTERM)) break; @@ -187,11 +245,21 @@ int main(int argc, char **argv) // Now that we're finished with anything privileged, switch over to running as "nobody" if (mStatus_NoError == err) { - const struct passwd *pw = getpwnam("nobody"); + const struct passwd *pw = getpwnam(MDNSD_USER); if (pw != NULL) + { + setgid(pw->pw_gid); setuid(pw->pw_uid); + } else - LogMsg("WARNING: mdnsd continuing as root because user \"nobody\" does not exist"); +#ifdef MDNSD_NOROOT + { + LogMsg("WARNING: mdnsd exiting because user \""MDNSD_USER"\" does not exist"); + err = mStatus_Invalid; + } +#else + LogMsg("WARNING: mdnsd continuing as root because user \""MDNSD_USER"\" does not exist"); +#endif } if (mStatus_NoError == err) @@ -250,9 +318,9 @@ asm(".desc ___crashreporter_info__, 0x10"); // For convenience when using the "strings" command, this is the last thing in the file #if mDNSResponderVersion > 1 -mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder-" STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")"; +mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder-" STRINGIFY(mDNSResponderVersion); #elif MDNS_VERSIONSTR_NODTS mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder (Engineering Build)"; #else -mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder (Engineering Build) (" __DATE__ " " __TIME__ ")"; +mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder (Engineering Build)"; #endif diff --git a/external/apache2/mDNSResponder/dist/mDNSPosix/mDNSPosix.c b/external/apache2/mDNSResponder/dist/mDNSPosix/mDNSPosix.c index a01a2544e143..ff76f07eee95 100755 --- a/external/apache2/mDNSResponder/dist/mDNSPosix/mDNSPosix.c +++ b/external/apache2/mDNSResponder/dist/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++; } } @@ -523,9 +543,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); @@ -1406,6 +1427,23 @@ mDNSexport mDNSs32 mDNSPlatformUTC(void) return time(NULL); } +mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID InterfaceID, char *EthAddr, char *IPAddr, int iteration) + { + (void) m; + (void) InterfaceID; + (void) EthAddr; + (void) IPAddr; + (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/external/apache2/mDNSResponder/dist/mDNSPosix/mDNSUNP.c b/external/apache2/mDNSResponder/dist/mDNSPosix/mDNSUNP.c index ff9c9fa16a09..1059c83cf157 100755 --- a/external/apache2/mDNSResponder/dist/mDNSPosix/mDNSUNP.c +++ b/external/apache2/mDNSResponder/dist/mDNSPosix/mDNSUNP.c @@ -55,7 +55,7 @@ #endif #if defined(AF_INET6) && HAVE_IPV6 && !HAVE_LINUX && !defined(sun) -#if !defined(__NetBSD__) +#if defined(__FreeBSD__) || defined(__DragonFly__) #include #endif #include @@ -278,6 +278,24 @@ struct ifi_info *get_ifi_info(int family, int doaliases) if ((flags & IFF_UP) == 0) continue; /* ignore if interface not up */ + /* Skip addresses we can't use */ +#ifdef SIOCGIFAFLAG_IN6 + if (ifr->ifr_addr.sa_family == AF_INET6) { + struct in6_ifreq ifr6; + + if (sockf6 == -1) + sockf6 = socket(AF_INET6, SOCK_DGRAM, 0); + memset(&ifr6, 0, sizeof(ifr6)); + memcpy(&ifr6.ifr_name, &ifr->ifr_name, sizeof(ifr6.ifr_name)); + memcpy(&ifr6.ifr_addr, &ifr->ifr_addr, sizeof(ifr6.ifr_addr)); + if (ioctl(sockf6, SIOCGIFAFLAG_IN6, &ifr6) < 0) + goto gotError; + if (ifr6.ifr_ifru.ifru_flags6 & + (IN6_IFF_NOTREADY | IN6_IFF_DETACHED)) + continue; + } +#endif + ifi = (struct ifi_info*)calloc(1, sizeof(struct ifi_info)); if (ifi == NULL) { goto gotError; diff --git a/external/apache2/mDNSResponder/dist/mDNSShared/PlatformCommon.c b/external/apache2/mDNSResponder/dist/mDNSShared/PlatformCommon.c index a2c74091a3a1..7a1985aa2017 100644 --- a/external/apache2/mDNSResponder/dist/mDNSShared/PlatformCommon.c +++ b/external/apache2/mDNSResponder/dist/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/external/apache2/mDNSResponder/dist/mDNSShared/dns-sd.1 b/external/apache2/mDNSResponder/dist/mDNSShared/dns-sd.1 index da9f37aeb766..1c48802ce503 100644 --- a/external/apache2/mDNSResponder/dist/mDNSShared/dns-sd.1 +++ b/external/apache2/mDNSResponder/dist/mDNSShared/dns-sd.1 @@ -16,7 +16,7 @@ .\" .Dd April 2004 \" Date .Dt dns-sd 1 \" Document Title -.Os Darwin \" Operating System +.Os NetBSD \" Operating System .\" .Sh NAME .Nm dns-sd @@ -43,11 +43,6 @@ The library API that .Nm uses is documented in .Pa /usr/include/dns_sd.h . -The -.Nm -command replaces the older -.Xr mDNS 1 -command. .Pp The .Nm @@ -181,14 +176,11 @@ window. .Pa /usr/bin/dns-sd \" Pathname .\" .Sh SEE ALSO -.Xr mDNS 1 -.Xr mDNSResponder 8 -.\" -.Sh BUGS -.Nm -bugs are tracked in Apple Radar component "mDNSResponder". +.Xr mdnsd 8 .\" .Sh HISTORY The .Nm -command first appeared in Mac OS X 10.4 (Tiger). +command first appeared in +.Nx 6.0 , +having originated in Mac OS X 10.4 (Tiger). diff --git a/external/apache2/mDNSResponder/dist/mDNSShared/dns_sd.h b/external/apache2/mDNSResponder/dist/mDNSShared/dns_sd.h index 7632a2292c61..62732b7a7a62 100644 --- a/external/apache2/mDNSResponder/dist/mDNSShared/dns_sd.h +++ b/external/apache2/mDNSResponder/dist/mDNSShared/dns_sd.h @@ -77,7 +77,7 @@ */ #ifndef _DNS_SD_H -#define _DNS_SD_H 2581400 +#define _DNS_SD_H 3201600 #ifdef __cplusplus extern "C" { @@ -341,7 +341,7 @@ enum * lock or take similar appropriate precautions to serialize those calls. */ - kDNSServiceFlagsSuppressUnusable = 0x8000 + kDNSServiceFlagsSuppressUnusable = 0x8000, /* * This flag is meaningful only in DNSServiceQueryRecord which suppresses unusable queries on the * wire. If "hostname" is a wide-area unicast DNS hostname (i.e. not a ".local." name) @@ -351,6 +351,26 @@ enum * "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(). */ @@ -488,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) */ @@ -599,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 @@ -2394,6 +2415,7 @@ DNSServiceErrorType DNSSD_API DNSServiceSetDispatchQueue #endif //__APPLE_API_PRIVATE +#if !defined(__NetBSD__) /* Some C compiler cleverness. We can make the compiler check certain things for us, * and report errors at compile-time if anything is wrong. The usual way to do this would * be to use a run-time "if" statement or the conventional run-time "assert" mechanism, but @@ -2405,6 +2427,7 @@ struct CompileTimeAssertionChecks_DNS_SD { char assert0[(sizeof(union _TXTRecordRef_t) == 16) ? 1 : -1]; }; +#endif #ifdef __cplusplus } diff --git a/external/apache2/mDNSResponder/dist/mDNSShared/dnssd_clientlib.c b/external/apache2/mDNSResponder/dist/mDNSShared/dnssd_clientlib.c index c3a3cfcc48a7..8abbb6d08962 100644 --- a/external/apache2/mDNSResponder/dist/mDNSShared/dnssd_clientlib.c +++ b/external/apache2/mDNSResponder/dist/mDNSShared/dnssd_clientlib.c @@ -363,4 +363,4 @@ DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex // NOT static -- otherwise the compiler may optimize it out // The "@(#) " pattern is a special prefix the "what" command looks for -const char VersionString_SCCS_libdnssd[] = "@(#) libdns_sd " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")"; +const char VersionString_SCCS_libdnssd[] = "@(#) libdns_sd " STRINGIFY(mDNSResponderVersion); diff --git a/external/apache2/mDNSResponder/dist/mDNSShared/dnssd_clientstub.c b/external/apache2/mDNSResponder/dist/mDNSShared/dnssd_clientstub.c index 62f640e25269..077f70c16493 100644 --- a/external/apache2/mDNSResponder/dist/mDNSShared/dnssd_clientstub.c +++ b/external/apache2/mDNSResponder/dist/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 @@ -165,14 +170,14 @@ static int write_all(dnssd_sock_t sd, char *buf, size_t len) if (getsockopt(sd, SOL_SOCKET, SO_ISDEFUNCT, &defunct, &dlen) < 0) syslog(LOG_WARNING, "dnssd_clientstub write_all: SO_ISDEFUNCT failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); if (!defunct) - syslog(LOG_WARNING, "dnssd_clientstub write_all(%d) failed %ld/%ld %d %s", sd, + syslog(LOG_WARNING, "dnssd_clientstub write_all(%d) failed %zd/%ld %d %s", sd, (long)num_written, (long)len, (num_written < 0) ? dnssd_errno : 0, (num_written < 0) ? dnssd_strerror(dnssd_errno) : ""); else syslog(LOG_INFO, "dnssd_clientstub write_all(%d) DEFUNCT", sd); #else - syslog(LOG_WARNING, "dnssd_clientstub write_all(%d) failed %ld/%ld %d %s", sd, + syslog(LOG_WARNING, "dnssd_clientstub write_all(%d) failed %zd/%ld %d %s", sd, (long)num_written, (long)len, (num_written < 0) ? dnssd_errno : 0, (num_written < 0) ? dnssd_strerror(dnssd_errno) : ""); @@ -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; @@ -445,7 +485,7 @@ static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags f else { #ifdef SO_NOSIGPIPE - const unsigned long optval = 1; + int optval = 1; #endif *ref = NULL; sdr->sockfd = socket(AF_DNSSD, SOCK_STREAM, 0); @@ -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); @@ -1094,6 +1141,16 @@ DNSServiceErrorType DNSSD_API DNSServiceResolve if (!name || !regtype || !domain || !callBack) return kDNSServiceErr_BadParam; + // Need a real InterfaceID for WakeOnResolve + if ((flags & kDNSServiceFlagsWakeOnResolve) != 0 && + ((interfaceIndex == kDNSServiceInterfaceIndexAny) || + (interfaceIndex == kDNSServiceInterfaceIndexLocalOnly) || + (interfaceIndex == kDNSServiceInterfaceIndexUnicast) || + (interfaceIndex == kDNSServiceInterfaceIndexP2P))) + { + 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/external/apache2/mDNSResponder/dist/mDNSShared/dnssd_ipc.h b/external/apache2/mDNSResponder/dist/mDNSShared/dnssd_ipc.h index 760e1b4bc7c7..772e51bae562 100644 --- a/external/apache2/mDNSResponder/dist/mDNSShared/dnssd_ipc.h +++ b/external/apache2/mDNSResponder/dist/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 @@ -104,7 +106,10 @@ extern char *win32_strerror(int inErrorCode); // structures correctly anyway, so a plain "struct" is usually fine. In the event that structures are not packed // correctly, our compile-time assertion checks will catch it and prevent inadvertent generation of non-working code. #ifndef packedstruct - #if ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 9))) + #ifdef __packed + #define packedstruct struct __packed + #define packedunion union __packed + #elif ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 9))) #define packedstruct struct __attribute__((__packed__)) #define packedunion union __attribute__((__packed__)) #else @@ -116,7 +121,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/external/apache2/mDNSResponder/dist/mDNSShared/uds_daemon.c b/external/apache2/mDNSResponder/dist/mDNSShared/uds_daemon.c index aff5ff0f0d53..55aaa1352ed3 100644 --- a/external/apache2/mDNSResponder/dist/mDNSShared/uds_daemon.c +++ b/external/apache2/mDNSResponder/dist/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 { @@ -237,7 +245,10 @@ typedef struct reply_state // globals mDNSexport mDNS mDNSStorage; -mDNSexport const char ProgramName[] = "mDNSResponder"; +#ifndef PROGRAM_NAME +#define PROGRAM_NAME "mDNSResponder" +#endif +mDNSexport const char ProgramName[] = PROGRAM_NAME; static dnssd_sock_t listenfd = dnssd_InvalidSocket; static request_state *all_requests = NULL; @@ -275,7 +286,7 @@ mDNSexport DNameListElem *AutoBrowseDomains; // List created from those local-o mDNSlocal void FatalError(char *errmsg) { LogMsg("%s: %s", errmsg, dnssd_strerror(dnssd_errno)); - *(long*)0 = 0; // On OS X abort() doesn't generate a crash log, but writing to zero does + *(volatile long*)0 = 0; // On OS X abort() doesn't generate a crash log, but writing to zero does abort(); // On platforms where writing to zero doesn't generate an exception, abort instead } @@ -415,7 +426,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 +469,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 +495,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 +514,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 +616,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 +645,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 +802,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 +872,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 +901,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 +919,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 +997,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 +1018,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 +1028,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 +1101,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 +1134,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 +1172,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 +1240,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 +1424,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 +1441,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; + if (interfaceID == mDNSInterface_P2P) + { + interfaceID = mDNSInterface_Any; + regFlags |= regFlagIncludeP2P; + } + else if (request->flags & kDNSServiceFlagsIncludeP2P) + regFlags |= regFlagIncludeP2P; + + // 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 +1505,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 +1606,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 +1691,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 +1759,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 +1790,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 +1807,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 +1901,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 +2008,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); @@ -2040,8 +2133,9 @@ mDNSlocal mStatus handle_browse_request(request_state *request) if (!request->msgptr) { LogMsg("%3d: DNSServiceBrowse(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } - if (domain[0] == '\0') uDNS_RegisterSearchDomains(&mDNSStorage); + 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 +2154,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 +2232,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 +2270,7 @@ mDNSlocal mStatus handle_resolve_request(request_state *request) mDNSBool wasP2P = (interfaceIndex == kDNSServiceInterfaceIndexP2P); + request->flags = flags; if (wasP2P) interfaceIndex = kDNSServiceInterfaceIndexAny; InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); @@ -2203,6 +2299,13 @@ mDNSlocal mStatus handle_resolve_request(request_state *request) request->u.resolve.qsrv.ExpectUnique = mDNStrue; 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.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; @@ -2216,6 +2319,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.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; @@ -2238,8 +2347,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); + } } } @@ -2257,6 +2370,241 @@ 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; + // Always set the QuestionContext to indicate that this question should be stopped + // before freeing. Don't rely on "q". + q2->QuestionContext = request; + // 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]; @@ -2265,37 +2613,178 @@ 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) { - mDNS_StopQuery(&mDNSStorage, question); - question->QuestionCallback = mDNSNULL; - // If we got a non-negative answer for our "local SOA" test query, start an additional parallel unicast query - if (answer->RecordType != kDNSRecordTypePacketNegative) - { - *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); - } + 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)) + { + 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 + // + // 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)) + { + // 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; + } + } + // 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)) + { + mDNSu16 qtype; + // Sanity check: "q" will be set only if "question" is the .local unicast query. + if (!q) + { + LogMsg("queryrecord_result_callback: ERROR!! answering multicast question with unicast cache record"); + return; + } + // Deliver negative response for A/AAAA if there was a positive response for AAAA/A respectively. + if (question->qtype != kDNSType_A && question->qtype != kDNSType_AAAA) + { + LogInfo("queryrecord_result_callback:Question %##s (%s) not answering local question with negative unicast response", question->qname.c, DNSTypeName(question->qtype)); + return; + } + qtype = (question->qtype == kDNSType_A ? kDNSType_AAAA : kDNSType_A); + if (!mDNS_CheckForCacheRecord(m, question, qtype)) + { + LogInfo("queryrecord_result_callback:Question %##s (%s) not answering local question with negative unicast response (can't find positive record)", question->qname.c, DNSTypeName(question->qtype)); + return; + } + LogInfo("queryrecord_result_callback:Question %##s (%s) answering local with negative unicast response (found positive record)", question->qname.c, DNSTypeName(question->qtype)); + } + 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); @@ -2314,7 +2803,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]; @@ -2332,6 +2828,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) @@ -2409,10 +2912,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) @@ -2434,6 +2975,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; @@ -2448,9 +2990,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->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); @@ -2458,46 +3031,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); @@ -2588,7 +3130,7 @@ mDNSlocal mStatus handle_enum_request(request_state *request) { LogMsg("%3d: DNSServiceEnumerateDomains(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } // allocate context structures - uDNS_RegisterSearchDomains(&mDNSStorage); + uDNS_SetupSearchDomains(&mDNSStorage, UDNS_START_WAB_QUERY); #if 0 // mark which kind of enumeration we're doing so we can (de)authorize certain domains @@ -2636,7 +3178,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); @@ -2718,7 +3260,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]; @@ -2807,12 +3349,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) @@ -2858,10 +3442,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.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); @@ -2870,11 +3473,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); @@ -2889,6 +3507,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", @@ -2998,11 +3619,14 @@ mDNSlocal void read_msg(request_state *req) #if !defined(_WIN32) cmsg = CMSG_FIRSTHDR(&msg); #if DEBUG_64BIT_SCM_RIGHTS - LogMsg("%3d: Expecting %d %d %d %d", req->sd, sizeof(cbuf), sizeof(cbuf), SOL_SOCKET, SCM_RIGHTS); - LogMsg("%3d: Got %d %d %d %d", req->sd, msg.msg_controllen, cmsg->cmsg_len, cmsg->cmsg_level, cmsg->cmsg_type); + LogMsg("%3d: Expecting %d %d %d %d", req->sd, sizeof(cbuf), CMSG_LEN(sizeof(dnssd_sock_t)), SOL_SOCKET, SCM_RIGHTS); + if (cmsg) + LogMsg("%3d: Got %d %d %d %d", req->sd, msg.msg_controllen, cmsg->cmsg_len, cmsg->cmsg_level, cmsg->cmsg_type); + else + LogMsg("%3d: Got %d NULL", req->sd, msg.msg_controllen); #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) { @@ -3125,124 +3749,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; } - 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) + if (req->hdr.version != VERSION) { - 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; + LogMsg("ERROR: client version %d incompatible with daemon version %d", req->hdr.version, VERSION); + AbortUnlinkAndFree(req); + return; } - } - // 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; + 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; + } } mDNSlocal void connect_callback(int fd, short filter, void *info) @@ -3250,7 +3877,9 @@ mDNSlocal void connect_callback(int fd, short filter, void *info) dnssd_sockaddr_t cliaddr; dnssd_socklen_t len = (dnssd_socklen_t) sizeof(cliaddr); dnssd_sock_t sd = accept(fd, (struct sockaddr*) &cliaddr, &len); -#if defined(SO_NOSIGPIPE) || defined(_WIN32) +#if defined(SO_NOSIGPIPE) + int optval = 1; +#elif defined(_WIN32) unsigned long optval = 1; #endif @@ -3548,6 +4177,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; @@ -3576,9 +4286,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", @@ -3644,6 +4354,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); @@ -3731,7 +4447,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 @@ -3741,8 +4457,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 @@ -3771,15 +4487,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) @@ -3802,6 +4520,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) @@ -3831,7 +4552,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); + LogTimer("m->NextScheduledStopTime ", m->NextScheduledStopTime); } #if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING @@ -4015,10 +4736,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) <= 1016) ? 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/external/apache2/mDNSResponder/dist/mDNSShared/uds_daemon.h b/external/apache2/mDNSResponder/dist/mDNSShared/uds_daemon.h index 93cfd1e96598..1e17efa10539 100644 --- a/external/apache2/mDNSResponder/dist/mDNSShared/uds_daemon.h +++ b/external/apache2/mDNSResponder/dist/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);