mDNSResponder-320.5.tar.gz mac-os-x-107 v320.5
authorApple <opensource@apple.com>
Fri, 13 May 2011 22:51:13 +0000 (22:51 +0000)
committerApple <opensource@apple.com>
Fri, 13 May 2011 22:51:13 +0000 (22:51 +0000)
46 files changed:
Clients/FirefoxExtension/extension/content/bonjour4firefox.xul
Clients/dns-sd.c
Clients/mDNSNetMonitor.VisualStudio/mDNSNetMonitor.vcproj
Makefile
mDNSCore/DNSCommon.c
mDNSCore/DNSCommon.h
mDNSCore/DNSDigest.c
mDNSCore/mDNS.c
mDNSCore/mDNSEmbeddedAPI.h
mDNSCore/uDNS.c
mDNSCore/uDNS.h
mDNSMacOS9/SubTypeTester.c
mDNSMacOSX/BonjourEvents-Info.plist [new file with mode: 0644]
mDNSMacOSX/BonjourEvents.c [new file with mode: 0644]
mDNSMacOSX/P2PPacketFilter.c [new file with mode: 0644]
mDNSMacOSX/P2PPacketFilter.h [new file with mode: 0644]
mDNSMacOSX/daemon.c
mDNSMacOSX/helper-stubs.c
mDNSMacOSX/helper.c
mDNSMacOSX/helper.h
mDNSMacOSX/helpermsg.defs
mDNSMacOSX/mDNSMacOSX.c
mDNSMacOSX/mDNSMacOSX.h
mDNSMacOSX/mDNSResponder.sb
mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj
mDNSPosix/Identify.c
mDNSPosix/Makefile
mDNSPosix/NetMonitor.c
mDNSPosix/ProxyResponder.c
mDNSPosix/Responder.c
mDNSPosix/mDNSPosix.c
mDNSResponder.sln
mDNSShared/PlatformCommon.c
mDNSShared/dns_sd.h
mDNSShared/dnsextd.c
mDNSShared/dnssd_clientshim.c
mDNSShared/dnssd_clientstub.c
mDNSShared/dnssd_ipc.h
mDNSShared/uds_daemon.c
mDNSShared/uds_daemon.h
mDNSWindows/DLL/dnssd.vcproj
mDNSWindows/DLLStub/DLLStub.vcproj
mDNSWindows/SystemService/Service.c
mDNSWindows/SystemService/Service.vcproj
mDNSWindows/mDNSWin32.c
mDNSWindows/mDNSWin32.h

index 7fd03929bb5c72acd5dc70aa086d1dd000d3da32..69e5efe224f997b1c8b15a0ad7f1bc72347bf027 100755 (executable)
 
                                if ( treeBrowseList.currentIndex == 0 )
                                {
-                                       window._content.location="http://www.apple.com/bonjour";
+                                       window._content.location="http://www.apple.com/macosx/features/bonjour";
                                }
                                else
                                {
index 826245b9424e8ef24863c1d98ee2ac56ed17e582..bf6cc1039057a7ba6a8f3e17f62a3b8971158e3a 100644 (file)
@@ -736,7 +736,12 @@ static void DNSSD_API qr_reply(DNSServiceRef sdref, const DNSServiceFlags flags,
        if (errorCode)
                {
                if (errorCode == kDNSServiceErr_NoSuchRecord) printf("No Such Record");
-               else                                          printf("Error code %d", errorCode);
+               else if (errorCode == kDNSServiceErr_Timeout)
+                       {
+                       printf("No Such Record\n");
+                       printf("Query Timed Out\n");
+                       exit(1);
+                       }
                }
        printf("\n");
 
@@ -947,7 +952,7 @@ static void getip(const char *const name, struct sockaddr_storage *result)
        if (addrs) freeaddrinfo(addrs);
        }
 
-static DNSServiceErrorType RegisterProxyAddressRecord(DNSServiceRef sdref, const char *host, const char *ip)
+static DNSServiceErrorType RegisterProxyAddressRecord(DNSServiceRef sdref, const char *host, const char *ip, DNSServiceFlags flags)
        {
        // Call getip() after the call DNSServiceCreateConnection().
        // On the Win32 platform, WinSock must be initialized for getip() to succeed.
@@ -955,11 +960,12 @@ static DNSServiceErrorType RegisterProxyAddressRecord(DNSServiceRef sdref, const
        // DNSServiceCreateConnection() is called before getip() is.
        struct sockaddr_storage hostaddr;
        getip(ip, &hostaddr);
+       flags |= kDNSServiceFlagsUnique;
        if (hostaddr.ss_family == AF_INET)
-               return(DNSServiceRegisterRecord(sdref, &record, kDNSServiceFlagsUnique, opinterface, host,
+               return(DNSServiceRegisterRecord(sdref, &record, flags, opinterface, host,
                        kDNSServiceType_A,    kDNSServiceClass_IN,  4, &((struct sockaddr_in *)&hostaddr)->sin_addr,  240, MyRegisterRecordCallback, (void*)host));
        else if (hostaddr.ss_family == AF_INET6)
-               return(DNSServiceRegisterRecord(sdref, &record, kDNSServiceFlagsUnique, opinterface, host,
+               return(DNSServiceRegisterRecord(sdref, &record, flags, opinterface, host,
                        kDNSServiceType_AAAA, kDNSServiceClass_IN, 16, &((struct sockaddr_in6*)&hostaddr)->sin6_addr, 240, MyRegisterRecordCallback, (void*)host));
        else return(kDNSServiceErr_BadParam);
        }
@@ -971,9 +977,8 @@ static DNSServiceErrorType RegisterProxyAddressRecord(DNSServiceRef sdref, const
 #define HexPair(P) ((HexVal((P)[0]) << 4) | HexVal((P)[1]))
 
 static DNSServiceErrorType RegisterService(DNSServiceRef *sdref,
-       const char *nam, const char *typ, const char *dom, const char *host, const char *port, int argc, char **argv)
+       const char *nam, const char *typ, const char *dom, const char *host, const char *port, int argc, char **argv, DNSServiceFlags flags)
        {
-       DNSServiceFlags flags = 0;
        uint16_t PortAsNumber = atoi(port);
        Opaque16 registerPort = { { PortAsNumber >> 8, PortAsNumber & 0xFF } };
        unsigned char txt[2048] = "";
@@ -1008,7 +1013,7 @@ static DNSServiceErrorType RegisterService(DNSServiceRef *sdref,
        
        //flags |= kDNSServiceFlagsAllowRemoteQuery;
        //flags |= kDNSServiceFlagsNoAutoRename;
-       
+
        return(DNSServiceRegister(sdref, flags, opinterface, nam, typ, dom, host, registerPort.NotAnInteger, (uint16_t) (ptr-txt), txt, reg_reply, NULL));
        }
 
@@ -1025,6 +1030,7 @@ int main(int argc, char **argv)
        DNSServiceErrorType err;
        char buffer[TypeBufferSize], *typ, *dom;
        int opi;
+       DNSServiceFlags flags = 0;
 
        // Extract the program name from argv[0], which by convention contains the path to this executable.
        // Note that this is just a voluntary convention, not enforced by the kernel --
@@ -1063,6 +1069,14 @@ int main(int argc, char **argv)
                printf("Using P2P\n");
                }
 
+       if (argc > 1 && !strcasecmp(argv[1], "-includep2p"))
+               {
+               argc--;
+               argv++;
+               flags |= kDNSServiceFlagsIncludeP2P;
+               printf("Including P2P\n");
+               }
+
        if (argc > 2 && !strcmp(argv[1], "-i"))
                {
                opinterface = if_nametoindex(argv[2]);
@@ -1073,7 +1087,7 @@ int main(int argc, char **argv)
                }
 
        if (argc < 2) goto Fail;        // Minimum command line is the command name and one argument
-       operation = getfirstoption(argc, argv, "EFBZLlRPQqCAUNTMISV"
+       operation = getfirstoption(argc, argv, "EFBZLlRPQqtCAUNTMISV"
                                                                #if HAS_NAT_PMP_API
                                                                        "X"
                                                                #endif
@@ -1104,7 +1118,7 @@ int main(int argc, char **argv)
                                        typ = gettype(buffer, typ);
                                        if (dom[0] == '.' && dom[1] == 0) dom[0] = 0;   // We allow '.' on the command line as a synonym for empty string
                                        printf("Browsing for %s%s%s\n", typ, dom[0] ? "." : "", dom);
-                                       err = DNSServiceBrowse(&client, 0, opinterface, typ, dom, browse_reply, NULL);
+                                       err = DNSServiceBrowse(&client, flags, opinterface, typ, dom, browse_reply, NULL);
                                        break;
 
                case 'Z':       typ = (argc < opi+1) ? "" : argv[opi+0];
@@ -1136,26 +1150,25 @@ int main(int argc, char **argv)
                                        dom = (argc < opi+3) ? "" : argv[opi+2];
                                        typ = gettype(buffer, typ);
                                        if (dom[0] == '.' && dom[1] == 0) dom[0] = 0;   // We allow '.' on the command line as a synonym for empty string
-                                       err = RegisterService(&client, argv[opi+0], typ, dom, NULL, argv[opi+3], argc-(opi+4), argv+(opi+4));
+                                       err = RegisterService(&client, argv[opi+0], typ, dom, NULL, argv[opi+3], argc-(opi+4), argv+(opi+4), flags);
                                        break;
 
                case 'P':       if (argc < opi+6) goto Fail;
                                        err = DNSServiceCreateConnection(&client_pa);
                                        if (err) { fprintf(stderr, "DNSServiceCreateConnection returned %d\n", err); return(err); }
-                                       err = RegisterProxyAddressRecord(client_pa, argv[opi+4], argv[opi+5]);
-                                       //err = RegisterProxyAddressRecord(client_pa, "two", argv[opi+5]);
+                                       err = RegisterProxyAddressRecord(client_pa, argv[opi+4], argv[opi+5], flags);
                                        if (err) break;
-                                       err = RegisterService(&client, argv[opi+0], gettype(buffer, argv[opi+1]), argv[opi+2], argv[opi+4], argv[opi+3], argc-(opi+6), argv+(opi+6));
-                                       //DNSServiceRemoveRecord(client_pa, record, 0);
-                                       //DNSServiceRemoveRecord(client_pa, record, 0);
+                                       err = RegisterService(&client, argv[opi+0], gettype(buffer, argv[opi+1]), argv[opi+2], argv[opi+4], argv[opi+3], argc-(opi+6), argv+(opi+6), flags);
                                        break;
 
+               case 't':
                case 'q':
                case 'Q':
                case 'C':       {
                                        uint16_t rrtype, rrclass;
-                                       DNSServiceFlags flags = kDNSServiceFlagsReturnIntermediates;
+                                       flags |= kDNSServiceFlagsReturnIntermediates;
                                        if (operation == 'q') flags |= kDNSServiceFlagsSuppressUnusable;
+                                       if (operation == 't') flags |= (kDNSServiceFlagsSuppressUnusable | kDNSServiceFlagsTimeout);
                                        if (argc < opi+1) goto Fail;
                                        rrtype = (argc <= opi+1) ? kDNSServiceType_A  : GetRRType(argv[opi+1]);
                                        rrclass = (argc <= opi+2) ? kDNSServiceClass_IN : atoi(argv[opi+2]);
@@ -1191,8 +1204,8 @@ int main(int argc, char **argv)
                                        static const char TXT1[] = "\xC" "First String"  "\xD" "Second String" "\xC" "Third String";
                                        static const char TXT2[] = "\xD" "Fourth String" "\xC" "Fifth String"  "\xC" "Sixth String";
                                        printf("Registering Service Test._testdualtxt._tcp.local.\n");
-                                       err = DNSServiceRegister(&client, 0, opinterface, "Test", "_testdualtxt._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT1)-1, TXT1, reg_reply, NULL);
-                                       if (!err) err = DNSServiceAddRecord(client, &record, 0, kDNSServiceType_TXT, sizeof(TXT2)-1, TXT2, 0);
+                                       err = DNSServiceRegister(&client, flags, opinterface, "Test", "_testdualtxt._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT1)-1, TXT1, reg_reply, NULL);
+                                       if (!err) err = DNSServiceAddRecord(client, &record, flags, kDNSServiceType_TXT, sizeof(TXT2)-1, TXT2, 0);
                                        break;
                                        }
 
index 122889c1de034634b5d1d28a86940afff8362cd6..82e17e38272830eecad1dfb524d8fd7fa05db0e2 100755 (executable)
@@ -40,7 +40,7 @@
                        <Tool\r
                                Name="VCCLCompilerTool"\r
                                Optimization="0"\r
-                               AdditionalIncludeDirectories="../../mDNSWindows/SystemService;../../mDNSWindows;../../mDNSShared;../../mDNSCore;&quot;C:/Program Files/Microsoft SDKs/Windows/v6.1/Include&quot;"\r
+                               AdditionalIncludeDirectories="../../mDNSWindows/SystemService;../../mDNSWindows;../../mDNSShared;../../mDNSCore;&quot;$(VCInstallDir)include&quot;;&quot;$(VCInstallDir)atlmfc\include&quot;;&quot;C:/Program Files/Microsoft SDKs/Windows/v6.1/Include&quot;"\r
                                PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;_WIN32_WINNT=0x0501;MDNS_DEBUGMSGS=0;TARGET_OS_WIN32;WIN32_LEAN_AND_MEAN;USE_TCP_LOOPBACK;PLATFORM_NO_STRSEP;PLATFORM_NO_EPIPE;PLATFORM_NO_RLIMIT;PID_FILE=&quot;&quot;&quot;&quot;;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1;_USE_32BIT_TIME_T"\r
                                MinimalRebuild="true"\r
                                BasicRuntimeChecks="3"\r
                        />\r
                        <Tool\r
                                Name="VCCLCompilerTool"\r
-                               AdditionalIncludeDirectories="../../mDNSWindows/SystemService;../../mDNSWindows;../../mDNSShared;../../mDNSCore;&quot;C:/Program Files/Microsoft SDKs/Windows/v6.1/Include&quot;"\r
+                               AdditionalIncludeDirectories="../../mDNSWindows/SystemService;../../mDNSWindows;../../mDNSShared;../../mDNSCore;&quot;$(VCInstallDir)include&quot;;&quot;$(VCInstallDir)atlmfc\include&quot;;&quot;C:/Program Files/Microsoft SDKs/Windows/v6.1/Include&quot;"\r
                                PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_WIN32_WINNT=0x0501;MDNS_DEBUGMSGS=0;TARGET_OS_WIN32;WIN32_LEAN_AND_MEAN;USE_TCP_LOOPBACK;PLATFORM_NO_STRSEP;PLATFORM_NO_EPIPE;PLATFORM_NO_RLIMIT;PID_FILE=&quot;&quot;&quot;&quot;;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1;_USE_32BIT_TIME_T"\r
                                RuntimeLibrary="2"\r
                                UsePrecompiledHeader="0"\r
index c8c8ef9d4d995e82886a1036e7a831c2eceb14f5..bdd3b25ac29259248fb8652337e6845332381993 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -16,7 +16,7 @@
 
 include /Developer/Makefiles/pb_makefiles/platform.make
 
-MVERS = "mDNSResponder-258.21"
+MVERS = "mDNSResponder-320.5"
 
 DDNSWRITECONFIG = "$(DSTROOT)/Library/Application Support/Bonjour/ddnswriteconfig"
 
@@ -35,7 +35,7 @@ installsrc:
        ditto . "$(SRCROOT)"
 
 installhdrs::
-       cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild installhdrs OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) -target SystemLibraries
+       cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild installhdrs OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) SDKROOT=$(SDKROOT)  -target SystemLibraries
 
 clean::
        echo clean
index b0fe39d70d7c0b477f5b0bb90e857b4caf0f5b8b..2c2392558888c1bb1d0dc965ced467a4f1885673 100644 (file)
@@ -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,7 +1098,12 @@ mDNSexport void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID I
        q->ForceMCast          = mDNSfalse;
        q->ReturnIntermed      = mDNSfalse;
        q->SuppressUnusable    = mDNSfalse;
-       q->WakeOnResolve       = mDNSfalse;
+       q->SearchListIndex     = 0;
+       q->AppendSearchDomains = 0;
+       q->RetryWithSearchDomains = mDNSfalse;
+       q->TimeoutQuestion     = 0;
+       q->WakeOnResolve       = 0;
+       q->qnameOrig           = mDNSNULL;
        q->QuestionCallback    = callback;
        q->QuestionContext     = context;
        }
@@ -1187,6 +1216,13 @@ mDNSexport mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBod
 
 mDNSexport mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
        {
+       // LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records
+       // are handled in LocalOnlyRecordAnswersQuestion
+       if ((rr->InterfaceID == mDNSInterface_LocalOnly) || (rr->InterfaceID == mDNSInterface_P2P))
+               {
+               LogMsg("SameNameRecordAnswersQuestion: ERROR!! called with LocalOnly ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID);
+               return mDNSfalse;
+               }
        if (rr->InterfaceID &&
                q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly &&
                rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
@@ -1206,6 +1242,14 @@ mDNSexport mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr
 
 mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
        {
+       // LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records
+       // are handled in LocalOnlyRecordAnswersQuestion
+       if ((rr->InterfaceID == mDNSInterface_LocalOnly) || (rr->InterfaceID == mDNSInterface_P2P))
+               {
+               LogMsg("ResourceRecordAnswersQuestion: ERROR!! called with LocalOnly/P2P ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID);
+               return mDNSfalse;
+               }
+
        if (rr->InterfaceID &&
                q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly &&
                rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
@@ -1214,10 +1258,6 @@ mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr
        if (!rr->InterfaceID && rr->rDNSServer != q->qDNSServer) return(mDNSfalse);
 
        // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question.
-       // This also covers the case where the ResourceRecord is mDNSInterface_LocalOnly and the question is expecting a unicast
-       // DNS response. We don't want a local process to be able to create a fake LocalOnly address record for "www.bigbank.com"
-       // which would then cause other applications (e.g. Safari) to connect to the wrong address. If we decide to support this later,
-       // the restrictions need to be at least as strict as the restrictions on who can edit /etc/hosts and put fake addresses there.
        if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
 
        // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
@@ -1227,8 +1267,91 @@ mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr
        return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
        }
 
+// We have a separate function to handle LocalOnly AuthRecords because they can be created with
+// a valid InterfaceID (e.g., scoped /etc/hosts) and can be used to answer unicast questions unlike
+// multicast resource records (which has a valid InterfaceID) which can't be used to answer
+// unicast questions. ResourceRecordAnswersQuestion/SameNameRecordAnswersQuestion can't tell whether
+// a resource record is multicast or LocalOnly by just looking at the ResourceRecord because
+// LocalOnly records are truly identified by ARType in the AuthRecord.  As P2P and LocalOnly record
+// are kept in the same hash table, we use the same function to make it easy for the callers when
+// they walk the hash table to answer LocalOnly/P2P questions
+//
+mDNSexport mDNSBool LocalOnlyRecordAnswersQuestion(AuthRecord *const ar, const DNSQuestion *const q)
+       {
+       ResourceRecord *rr = &ar->resrec;
+       
+       // mDNSInterface_Any questions can be answered with LocalOnly/P2P records in this function. AuthRecord_Any
+       // records are handled in ResourceRecordAnswersQuestion/SameNameRecordAnswersQuestion
+       if (RRAny(ar))
+               {
+               LogMsg("LocalOnlyRecordAnswersQuestion: ERROR!! called with regular AuthRecordAny %##s", rr->name->c);
+               return mDNSfalse;
+               }
+               
+       // Questions with mDNSInterface_LocalOnly InterfaceID should be answered with all resource records that are
+       // *local* to the machine. These include resource records that have InterfaceID set to mDNSInterface_LocalOnly,
+       // mDNSInterface_Any and any other real InterfaceID. Hence, LocalOnly questions should not be checked against
+       // the InterfaceID in the resource record.
+       //
+       // mDNSInterface_Unicast does not indicate any scope and hence treat them like mDNSInterface_Any.
+
+       if (rr->InterfaceID && 
+               q->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly && q->InterfaceID != mDNSInterface_Unicast &&
+               rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
+
+       // Entries in /etc/hosts are added as LocalOnly resource records. The LocalOnly resource records
+       // may have a scope e.g., fe80::1%en0. The question may be scoped or not: the InterfaceID may be set
+       // to mDNSInterface_Any, mDNSInterface_LocalOnly or a real InterfaceID (scoped). 
+       //
+       // 1) Question: Any, LocalOnly Record: no scope. This question should be answered with this record.
+       //
+       // 2) Question: Any, LocalOnly Record: scoped.  This question should be answered with the record because
+       //    traditionally applications never specify scope e.g., getaddrinfo, but need to be able
+       //    to get to /etc/hosts entries.
+       //
+       // 3) Question: Scoped (LocalOnly or InterfaceID), LocalOnly Record: no scope. This is the inverse of (2).
+       //    If we register a LocalOnly record, we need to answer a LocalOnly question. If the /etc/hosts has a
+       //    non scoped entry, it may not make sense to answer a scoped question. But we can't tell these two
+       //    cases apart. As we currently answer LocalOnly question with LocalOnly record, we continue to do so.
+       //
+       // 4) Question: Scoped (LocalOnly or InterfaceID), LocalOnly Record: scoped. LocalOnly questions should be
+       //    answered with any resource record where as if it has a valid InterfaceID, the scope should match.
+       //
+       // (1) and (2) is bypassed because we check for a non-NULL InterfaceID above. For (3), the InterfaceID is NULL
+       // and hence bypassed above. For (4) we bypassed LocalOnly questions and checked the scope of the record
+       // against the question.
+       //
+       // For P2P, InterfaceIDs of the question and the record should match.
+
+       // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question.
+       // LocalOnly authoritative answers are exempt. LocalOnly authoritative answers are used for /etc/host entries.
+       // We don't want a local process to be able to create a fake LocalOnly address record for "www.bigbank.com" which would then
+       // cause other applications (e.g. Safari) to connect to the wrong address. The rpc to register records filters out records
+       // with names that don't end in local and have mDNSInterface_LocalOnly set.
+       //
+       // Note: The check is bypassed for LocalOnly and for P2P it is not needed as only .local records are registered and for
+       // a question to match its names, it also has to end in .local and that question can't be a unicast question (See
+       // Question_uDNS macro and its usage). As P2P does not enforce .local only registrations we still make this check
+       // and also makes it future proof.
+
+       if (ar->ARType != AuthRecordLocalOnly && rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
+
+       // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
+       if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse);
+       if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
+
+       return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
+       }
+
 mDNSexport mDNSBool AnyTypeRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
        {
+       // LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records
+       // are handled in LocalOnlyRecordAnswersQuestion
+       if ((rr->InterfaceID == mDNSInterface_LocalOnly) || (rr->InterfaceID == mDNSInterface_P2P))
+               {
+               LogMsg("AnyTypeRecordAnswersQuestion: ERROR!! called with LocalOnly ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID);
+               return mDNSfalse;
+               }
        if (rr->InterfaceID &&
                q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly &&
                rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
@@ -1747,7 +1870,7 @@ mDNSexport mDNSu8 *putZone(DNSMessage *const msg, mDNSu8 *ptr, mDNSu8 *limit, co
 mDNSexport mDNSu8 *putPrereqNameNotInUse(const domainname *const name, DNSMessage *const msg, mDNSu8 *const ptr, mDNSu8 *const end)
        {
        AuthRecord prereq;
-       mDNS_SetupResourceRecord(&prereq, mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, mDNSNULL, mDNSNULL);
+       mDNS_SetupResourceRecord(&prereq, mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, AuthRecordAny, mDNSNULL, mDNSNULL);
        AssignDomainName(&prereq.namestorage, name);
        prereq.resrec.rrtype = kDNSQType_ANY;
        prereq.resrec.rrclass = kDNSClass_NONE;
@@ -1817,7 +1940,7 @@ mDNSexport mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domain
 mDNSexport mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease)
        {
        AuthRecord rr;
-       mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
+       mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
        rr.resrec.rrclass    = NormalMaxDNSMessageData;
        rr.resrec.rdlength   = sizeof(rdataOPT);        // One option in this OPT record
        rr.resrec.rdestimate = sizeof(rdataOPT);
@@ -1832,7 +1955,7 @@ mDNSexport mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease)
 mDNSexport mDNSu8 *putUpdateLeaseWithLimit(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease, mDNSu8 *limit)
        {
        AuthRecord rr;
-       mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
+       mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
        rr.resrec.rrclass    = NormalMaxDNSMessageData;
        rr.resrec.rdlength   = sizeof(rdataOPT);        // One option in this OPT record
        rr.resrec.rdestimate = sizeof(rdataOPT);
@@ -1851,7 +1974,7 @@ mDNSexport mDNSu8 *putHINFO(const mDNS *const m, DNSMessage *const msg, mDNSu8 *
                mDNSu8 *h = hinfo.rdatastorage.u.data;
                mDNSu16 len = 2 + m->HIHardware.c[0] + m->HISoftware.c[0];
                mDNSu8 *newptr;
-               mDNS_SetupResourceRecord(&hinfo, mDNSNULL, mDNSInterface_Any, kDNSType_HINFO, 0, kDNSRecordTypeUnique, mDNSNULL, mDNSNULL);
+               mDNS_SetupResourceRecord(&hinfo, mDNSNULL, mDNSInterface_Any, kDNSType_HINFO, 0, kDNSRecordTypeUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
                AppendDomainLabel(&hinfo.namestorage, &m->hostlabel);
                AppendDomainName (&hinfo.namestorage, &authInfo->domain);
                hinfo.resrec.rroriginalttl = 0;
@@ -2594,6 +2717,7 @@ mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m)
                }
        if (m->NewLocalOnlyQuestions)                     return(m->timenow);
        if (m->NewLocalRecords && AnyLocalRecordReady(m)) return(m->timenow);
+       if (m->NewLocalOnlyRecords)                       return(m->timenow);
        if (m->SPSProxyListChanged)                       return(m->timenow);
        if (m->LocalRemoveEvents)                         return(m->timenow);
 
@@ -2619,12 +2743,13 @@ mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m)
                if (e - m->NextScheduledProbe    > 0) e = m->NextScheduledProbe;
                if (e - m->NextScheduledResponse > 0) e = m->NextScheduledResponse;
                }
-
+       if (e - m->NextScheduledStopTime > 0) e = m->NextScheduledStopTime;
        return(e);
        }
 
 mDNSexport void ShowTaskSchedulingError(mDNS *const m)
        {
+       AuthRecord *rr;
        mDNS_Lock(m);
 
        LogMsg("Task Scheduling Error: Continuously busy for more than a second");
@@ -2641,9 +2766,11 @@ mDNSexport void ShowTaskSchedulingError(mDNS *const m)
 
        if (m->NewLocalRecords)
                {
-               AuthRecord *rr = AnyLocalRecordReady(m);
+               rr = AnyLocalRecordReady(m);
                if (rr) LogMsg("Task Scheduling Error: NewLocalRecords %s", ARDisplayString(m, rr));
                }
+       
+       if (m->NewLocalOnlyRecords) LogMsg("Task Scheduling Error: NewLocalOnlyRecords");
 
        if (m->SPSProxyListChanged) LogMsg("Task Scheduling Error: SPSProxyListChanged");
        if (m->LocalRemoveEvents)   LogMsg("Task Scheduling Error: LocalRemoveEvents");
index 401d57102d640e098a7e6505a9276af7d2a95987..08c636d9c7536faffef6a5690b8632d1bae0b855 100644 (file)
@@ -165,6 +165,7 @@ extern mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr, co
 extern mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q);
 extern mDNSBool AnyTypeRecordAnswersQuestion (const ResourceRecord *const rr, const DNSQuestion *const q);
 extern mDNSBool UnicastResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q);
+extern mDNSBool LocalOnlyRecordAnswersQuestion(AuthRecord *const rr, const DNSQuestion *const q);
 extern mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate);
 extern mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd);
 
@@ -226,6 +227,7 @@ extern mDNSu8 *putHINFO(const mDNS *const m, DNSMessage *const msg, mDNSu8 *ptr,
 #pragma mark - DNS Message Parsing Functions
 #endif
 
+#define AuthHashSlot(X) (DomainNameHashValue(X) % AUTH_HASH_SLOTS)
 #define HashSlot(X) (DomainNameHashValue(X) % CACHE_HASH_SLOTS)
 extern mDNSu32 DomainNameHashValue(const domainname *const name);
 extern void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 rdlength);
index d3d0a5cdf7cfeef9776b518a5cc202e4cacb90c0..b4a0158cc336db45fd407698cbcc4366b0d2b74c 100644 (file)
@@ -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);
index 51cc3671eba6f1cd2fe5768f76481afc6e03848a..005983c930d0d059026d3edfadc7bd2a9f223f0e 100755 (executable)
@@ -75,6 +75,8 @@ void WCFConnectionDealloc(WCFConnection* c) __attribute__((weak_import));
 mDNSlocal void BeginSleepProcessing(mDNS *const m);
 mDNSlocal void RetrySPSRegistrations(mDNS *const m);
 mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *EthAddr, mDNSOpaque48 *password);
+mDNSlocal mDNSBool CacheRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q);
+mDNSlocal mDNSBool LocalRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q);
 mDNSlocal void mDNS_PurgeBeforeResolve(mDNS *const m, DNSQuestion *q);
 
 // ***************************************************************************
@@ -110,6 +112,30 @@ mDNSexport const char *const mDNS_DomainTypeNames[] =
 #pragma mark - General Utility Functions
 #endif
 
+// If there is a authoritative LocalOnly record that answers questions of type A, AAAA and CNAME
+// this returns true. Main use is to handle /etc/hosts records. 
+#define LORecordAnswersAddressType(rr) ((rr)->ARType == AuthRecordLocalOnly && \
+                                                                       (rr)->resrec.RecordType & kDNSRecordTypeUniqueMask && \
+                                                                       ((rr)->resrec.rrtype == kDNSType_A || (rr)->resrec.rrtype == kDNSType_AAAA || \
+                                                                       (rr)->resrec.rrtype == kDNSType_CNAME))
+
+#define FollowCNAME(q, rr, AddRecord)  (AddRecord && (q)->qtype != kDNSType_CNAME && \
+                                                                               (rr)->RecordType != kDNSRecordTypePacketNegative && \
+                                                                               (rr)->rrtype == kDNSType_CNAME)
+
+mDNSlocal void SetNextQueryStopTime(mDNS *const m, const DNSQuestion *const q)
+       {
+       if (m->mDNS_busy != m->mDNS_reentrancy+1)
+               LogMsg("SetNextQueryTime: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+
+#if ForceAlerts
+       if (m->mDNS_busy != m->mDNS_reentrancy+1) *(long*)0 = 0;
+#endif
+
+       if (m->NextScheduledStopTime - q->StopTime > 0)
+               m->NextScheduledStopTime = q->StopTime;
+       }
+
 mDNSexport void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q)
        {
        if (m->mDNS_busy != m->mDNS_reentrancy+1)
@@ -130,6 +156,169 @@ mDNSexport void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q)
                }
        }
 
+mDNSlocal void ReleaseAuthEntity(AuthHash *r, AuthEntity *e)
+       {
+#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING >= 1
+       unsigned int i;
+       for (i=0; i<sizeof(*e); i++) ((char*)e)[i] = 0xFF;
+#endif
+       e->next = r->rrauth_free;
+       r->rrauth_free = e;
+       r->rrauth_totalused--;
+       }
+
+mDNSlocal void ReleaseAuthGroup(AuthHash *r, AuthGroup **cp)
+       {
+       AuthEntity *e = (AuthEntity *)(*cp);
+       LogMsg("ReleaseAuthGroup:  Releasing AuthGroup %##s", (*cp)->name->c);
+       if ((*cp)->rrauth_tail != &(*cp)->members)
+               LogMsg("ERROR: (*cp)->members == mDNSNULL but (*cp)->rrauth_tail != &(*cp)->members)");
+       if ((*cp)->name != (domainname*)((*cp)->namestorage)) mDNSPlatformMemFree((*cp)->name);
+       (*cp)->name = mDNSNULL;
+       *cp = (*cp)->next;                      // Cut record from list
+       ReleaseAuthEntity(r, e);
+       }
+
+mDNSlocal AuthEntity *GetAuthEntity(AuthHash *r, const AuthGroup *const PreserveAG)
+       {
+       AuthEntity *e = mDNSNULL;
+
+       if (r->rrauth_lock) { LogMsg("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL); }
+       r->rrauth_lock = 1;
+       
+       if (!r->rrauth_free)
+               {
+               // We allocate just one AuthEntity at a time because we need to be able
+               // free them all individually which normally happens when we parse /etc/hosts into
+               // AuthHash where we add the "new" entries and discard (free) the already added
+               // entries. If we allocate as chunks, we can't free them individually.
+               AuthEntity *storage = mDNSPlatformMemAllocate(sizeof(AuthEntity));
+               storage->next = mDNSNULL;
+               r->rrauth_free = storage;
+               }
+       
+       // If we still have no free records, recycle all the records we can.
+       // Enumerating the entire auth is moderately expensive, so when we do it, we reclaim all the records we can in one pass.
+       if (!r->rrauth_free)
+               {
+               mDNSu32 oldtotalused = r->rrauth_totalused;
+               mDNSu32 slot;
+               for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+                       {
+                       AuthGroup **cp = &r->rrauth_hash[slot];
+                       while (*cp)
+                               {
+                               if ((*cp)->members || (*cp)==PreserveAG) cp=&(*cp)->next;
+                               else ReleaseAuthGroup(r, cp);
+                               }
+                       }
+               LogInfo("GetAuthEntity: Recycled %d records to reduce auth cache from %d to %d",
+                       oldtotalused - r->rrauth_totalused, oldtotalused, r->rrauth_totalused);
+               }
+
+       if (r->rrauth_free)     // If there are records in the free list, take one
+               {
+               e = r->rrauth_free;
+               r->rrauth_free = e->next;
+               if (++r->rrauth_totalused >= r->rrauth_report)
+                       {
+                       LogInfo("RR Auth now using %ld objects", r->rrauth_totalused);
+                       if      (r->rrauth_report <  100) r->rrauth_report += 10;
+                       else if (r->rrauth_report < 1000) r->rrauth_report += 100;
+                       else                               r->rrauth_report += 1000;
+                       }
+               mDNSPlatformMemZero(e, sizeof(*e));
+               }
+
+       r->rrauth_lock = 0;
+
+       return(e);
+       }
+
+mDNSexport AuthGroup *AuthGroupForName(AuthHash *r, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name)
+       {
+       AuthGroup *ag;
+       for (ag = r->rrauth_hash[slot]; ag; ag=ag->next)
+               if (ag->namehash == namehash && SameDomainName(ag->name, name))
+                       break;
+       return(ag);
+       }
+
+mDNSexport AuthGroup *AuthGroupForRecord(AuthHash *r, const mDNSu32 slot, const ResourceRecord *const rr)
+       {
+       return(AuthGroupForName(r, slot, rr->namehash, rr->name));
+       }
+
+mDNSlocal AuthGroup *GetAuthGroup(AuthHash *r, const mDNSu32 slot, const ResourceRecord *const rr)
+       {
+       mDNSu16 namelen = DomainNameLength(rr->name);
+       AuthGroup *ag = (AuthGroup*)GetAuthEntity(r, mDNSNULL);
+       if (!ag) { LogMsg("GetAuthGroup: Failed to allocate memory for %##s", rr->name->c); return(mDNSNULL); }
+       ag->next         = r->rrauth_hash[slot];
+       ag->namehash     = rr->namehash;
+       ag->members      = mDNSNULL;
+       ag->rrauth_tail  = &ag->members;
+       ag->name         = (domainname*)ag->namestorage;
+       ag->NewLocalOnlyRecords = mDNSNULL;
+       if (namelen > InlineCacheGroupNameSize) ag->name = mDNSPlatformMemAllocate(namelen);
+       if (!ag->name)
+               {
+               LogMsg("GetAuthGroup: Failed to allocate name storage for %##s", rr->name->c);
+               ReleaseAuthEntity(r, (AuthEntity*)ag);
+               return(mDNSNULL);
+               }
+       AssignDomainName(ag->name, rr->name);
+
+       if (AuthGroupForRecord(r, slot, rr)) LogMsg("GetAuthGroup: Already have AuthGroup for %##s", rr->name->c);
+       r->rrauth_hash[slot] = ag;
+       if (AuthGroupForRecord(r, slot, rr) != ag) LogMsg("GetAuthGroup: Not finding AuthGroup for %##s", rr->name->c);
+       
+       return(ag);
+       }
+
+// Returns the AuthGroup in which the AuthRecord was inserted
+mDNSexport AuthGroup *InsertAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr)
+       {
+       AuthGroup *ag;
+       const mDNSu32 slot = AuthHashSlot(rr->resrec.name);
+       ag = AuthGroupForRecord(r, slot, &rr->resrec);
+       if (!ag) ag = GetAuthGroup(r, slot, &rr->resrec);       // If we don't have a AuthGroup for this name, make one now
+       if (ag)
+               {
+               LogInfo("InsertAuthRecord: inserting auth record %s from table", ARDisplayString(m, rr));
+               *(ag->rrauth_tail) = rr;                                // Append this record to tail of cache slot list
+               ag->rrauth_tail = &(rr->next);                  // Advance tail pointer
+               }
+       return ag;
+       }
+
+mDNSexport AuthGroup *RemoveAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr)
+       {
+       AuthGroup *a;
+       AuthGroup **ag = &a;
+       AuthRecord **rp;
+       const mDNSu32 slot = AuthHashSlot(rr->resrec.name);
+
+       a = AuthGroupForRecord(r, slot, &rr->resrec);
+       if (!a) { LogMsg("RemoveAuthRecord: ERROR!! AuthGroup not found for %s", ARDisplayString(m, rr)); return mDNSNULL; }
+       rp = &(*ag)->members;
+       while (*rp)
+               {
+               if (*rp != rr)
+                       rp=&(*rp)->next;
+               else
+                       {
+                       // We don't break here, so that we can set the tail below without tracking "prev" pointers
+
+                       LogInfo("RemoveAuthRecord: removing auth record %s from table", ARDisplayString(m, rr));
+                       *rp = (*rp)->next;                      // Cut record from list
+                       }
+               }
+       // TBD: If there are no more members, release authgroup ?
+       (*ag)->rrauth_tail = rp;
+       return a;
+       }
+
 mDNSexport CacheGroup *CacheGroupForName(const mDNS *const m, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name)
        {
        CacheGroup *cg;
@@ -186,10 +375,83 @@ mDNSexport char *InterfaceNameForID(mDNS *const m, const mDNSInterfaceID Interfa
        return(intf ? intf->ifname : mDNSNULL);
        }
 
-// For a single given DNSQuestion, deliver an add/remove result for the single given AuthRecord
-// Used by AnswerAllLocalQuestionsWithLocalAuthRecord() and AnswerNewLocalOnlyQuestion()
-mDNSlocal void AnswerLocalQuestionWithLocalAuthRecord(mDNS *const m, DNSQuestion *q, AuthRecord *rr, QC_result AddRecord)
+// Caller should hold the lock
+mDNSlocal void GenerateNegativeResponse(mDNS *const m)
        {
+       DNSQuestion *q;
+       if (!m->CurrentQuestion) { LogMsg("GenerateNegativeResponse: ERROR!! CurrentQuestion not set"); return; }
+       q = m->CurrentQuestion;
+       LogInfo("GenerateNegativeResponse: Generating negative response for question %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+
+       MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, mDNSInterface_Any, mDNSNULL);
+       AnswerCurrentQuestionWithResourceRecord(m, &m->rec.r, QC_addnocache);
+       if (m->CurrentQuestion == q) { q->ThisQInterval = 0; }                          // Deactivate this question
+       // Don't touch the question after this
+       m->rec.r.resrec.RecordType = 0;         // Clear RecordType to show we're not still using it
+       }
+
+mDNSlocal void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, ResourceRecord *rr)
+       {
+       const mDNSBool selfref = SameDomainName(&q->qname, &rr->rdata->u.name);
+       if (q->CNAMEReferrals >= 10 || selfref)
+               LogMsg("AnswerQuestionByFollowingCNAME: %p %##s (%s) NOT following CNAME referral %d%s for %s",
+                       q, q->qname.c, DNSTypeName(q->qtype), q->CNAMEReferrals, selfref ? " (Self-Referential)" : "", RRDisplayString(m, rr));
+       else
+               {
+               const mDNSu32 c = q->CNAMEReferrals + 1;                // Stash a copy of the new q->CNAMEReferrals value
+
+               // The SameDomainName check above is to ignore bogus CNAME records that point right back at
+               // themselves. Without that check we can get into a case where we have two duplicate questions,
+               // A and B, and when we stop question A, UpdateQuestionDuplicates copies the value of CNAMEReferrals
+               // from A to B, and then A is re-appended to the end of the list as a duplicate of B (because
+               // the target name is still the same), and then when we stop question B, UpdateQuestionDuplicates
+               // copies the B's value of CNAMEReferrals back to A, and we end up not incrementing CNAMEReferrals
+               // for either of them. This is not a problem for CNAME loops of two or more records because in
+               // those cases the newly re-appended question A has a different target name and therefore cannot be
+               // a duplicate of any other question ('B') which was itself a duplicate of the previous question A.
+
+               // Right now we just stop and re-use the existing query. If we really wanted to be 100% perfect,
+               // and track CNAMEs coming and going, we should really create a subordinate query here,
+               // which we would subsequently cancel and retract if the CNAME referral record were removed.
+               // In reality this is such a corner case we'll ignore it until someone actually needs it.
+
+               LogInfo("AnswerQuestionByFollowingCNAME: %p %##s (%s) following CNAME referral %d for %s",
+                       q, q->qname.c, DNSTypeName(q->qtype), q->CNAMEReferrals, RRDisplayString(m, rr));
+
+               mDNS_StopQuery_internal(m, q);                                                          // Stop old query
+               AssignDomainName(&q->qname, &rr->rdata->u.name);                        // Update qname
+               q->qnamehash = DomainNameHashValue(&q->qname);                          // and namehash
+               // If a unicast query results in a CNAME that points to a .local, we need to re-try
+               // this as unicast. Setting the mDNSInterface_Unicast tells mDNS_StartQuery_internal
+               // to try this as unicast query even though it is a .local name
+               if (!mDNSOpaque16IsZero(q->TargetQID) && IsLocalDomain(&q->qname))
+                       {
+                       LogInfo("AnswerQuestionByFollowingCNAME: Resolving a .local CNAME %p %##s (%s) Record %s",
+                               q, q->qname.c, DNSTypeName(q->qtype), RRDisplayString(m, rr));
+                       q->InterfaceID = mDNSInterface_Unicast;
+                       }
+               mDNS_StartQuery_internal(m, q);                                                         // start new query
+               // Record how many times we've done this. We need to do this *after* mDNS_StartQuery_internal,
+               // because mDNS_StartQuery_internal re-initializes CNAMEReferrals to zero
+               q->CNAMEReferrals = c;
+               }
+       }
+
+// For a single given DNSQuestion pointed to by CurrentQuestion, deliver an add/remove result for the single given AuthRecord
+// Note: All the callers should use the m->CurrentQuestion to see if the question is still valid or not
+mDNSlocal void AnswerLocalQuestionWithLocalAuthRecord(mDNS *const m, AuthRecord *rr, QC_result AddRecord)
+       {
+       DNSQuestion *q = m->CurrentQuestion;
+       mDNSBool followcname;
+
+       if (!q)
+               {
+               LogMsg("AnswerLocalQuestionWithLocalAuthRecord: ERROR!! CurrentQuestion NULL while answering with %s", ARDisplayString(m, rr));
+               return;
+               }
+               
+       followcname = FollowCNAME(q, &rr->resrec, AddRecord);
+
        // We should not be delivering results for record types Unregistered, Deregistering, and (unverified) Unique
        if (!(rr->resrec.RecordType & kDNSRecordTypeActiveMask))
                {
@@ -204,11 +466,50 @@ mDNSlocal void AnswerLocalQuestionWithLocalAuthRecord(mDNS *const m, DNSQuestion
        if (q->QuestionCallback && !q->NoAnswer)
                {
                q->CurrentAnswers += AddRecord ? 1 : -1;
-               q->QuestionCallback(m, q, &rr->resrec, AddRecord);
+               if (LORecordAnswersAddressType(rr))
+                       {
+                       if (!followcname || q->ReturnIntermed)
+                               {
+                               // Don't send this packet on the wire as we answered from /etc/hosts
+                               q->ThisQInterval = 0;
+                               q->LOAddressAnswers += AddRecord ? 1 : -1;
+                               q->QuestionCallback(m, q, &rr->resrec, AddRecord);
+                               }
+                       mDNS_ReclaimLockAfterCallback();        // Decrement mDNS_reentrancy to block mDNS API calls again
+                       // The callback above could have caused the question to stop. Detect that
+                       // using m->CurrentQuestion
+                       if (followcname && m->CurrentQuestion == q)
+                               AnswerQuestionByFollowingCNAME(m, q, &rr->resrec);
+                       return;
+                       }
+               else
+                       q->QuestionCallback(m, q, &rr->resrec, AddRecord);
                }
        mDNS_ReclaimLockAfterCallback();        // Decrement mDNS_reentrancy to block mDNS API calls again
        }
 
+mDNSlocal void AnswerInterfaceAnyQuestionsWithLocalAuthRecord(mDNS *const m, AuthRecord *rr, QC_result AddRecord)
+       {
+       if (m->CurrentQuestion)
+               LogMsg("AnswerInterfaceAnyQuestionsWithLocalAuthRecord: ERROR m->CurrentQuestion already set: %##s (%s)",
+                       m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
+       m->CurrentQuestion = m->Questions;
+       while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
+               {
+               mDNSBool answered;
+               DNSQuestion *q = m->CurrentQuestion;
+               if (RRAny(rr))
+                       answered = ResourceRecordAnswersQuestion(&rr->resrec, q);
+               else
+                       answered = LocalOnlyRecordAnswersQuestion(rr, q);
+               if (answered)
+                       AnswerLocalQuestionWithLocalAuthRecord(m, rr, AddRecord);               // MUST NOT dereference q again
+               if (m->CurrentQuestion == q)    // If m->CurrentQuestion was not auto-advanced, do it ourselves now
+                       m->CurrentQuestion = q->next;
+               }
+       m->CurrentQuestion = mDNSNULL;
+       }
+
 // When a new local AuthRecord is created or deleted, AnswerAllLocalQuestionsWithLocalAuthRecord()
 // delivers the appropriate add/remove events to listening questions:
 // 1. It runs though all our LocalOnlyQuestions delivering answers as appropriate,
@@ -229,26 +530,25 @@ mDNSlocal void AnswerAllLocalQuestionsWithLocalAuthRecord(mDNS *const m, AuthRec
        m->CurrentQuestion = m->LocalOnlyQuestions;
        while (m->CurrentQuestion && m->CurrentQuestion != m->NewLocalOnlyQuestions)
                {
+               mDNSBool answered;
                DNSQuestion *q = m->CurrentQuestion;
-               m->CurrentQuestion = q->next;
-               if (ResourceRecordAnswersQuestion(&rr->resrec, q))
-                       AnswerLocalQuestionWithLocalAuthRecord(m, q, rr, AddRecord);                    // MUST NOT dereference q again
-               }
-
-       // If this AuthRecord is marked LocalOnly or P2P, then we want to deliver it to all local 'mDNSInterface_Any' questions
-       if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->resrec.InterfaceID == mDNSInterface_P2P)
-               {
-               m->CurrentQuestion = m->Questions;
-               while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
-                       {
-                       DNSQuestion *q = m->CurrentQuestion;
+               // We are called with both LocalOnly/P2P record or a regular AuthRecord
+               if (RRAny(rr))
+                       answered = ResourceRecordAnswersQuestion(&rr->resrec, q);
+               else
+                       answered = LocalOnlyRecordAnswersQuestion(rr, q);
+               if (answered)
+                       AnswerLocalQuestionWithLocalAuthRecord(m, rr, AddRecord);                       // MUST NOT dereference q again
+               if (m->CurrentQuestion == q)    // If m->CurrentQuestion was not auto-advanced, do it ourselves now
                        m->CurrentQuestion = q->next;
-                       if (ResourceRecordAnswersQuestion(&rr->resrec, q))
-                               AnswerLocalQuestionWithLocalAuthRecord(m, q, rr, AddRecord);            // MUST NOT dereference q again
-                       }
                }
 
        m->CurrentQuestion = mDNSNULL;
+
+       // If this AuthRecord is marked LocalOnly or P2P, then we want to deliver it to all local 'mDNSInterface_Any' questions
+       if (rr->ARType == AuthRecordLocalOnly || rr->ARType == AuthRecordP2P)
+               AnswerInterfaceAnyQuestionsWithLocalAuthRecord(m, rr, AddRecord);
+
        }
 
 // ***************************************************************************
@@ -521,7 +821,7 @@ mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr)
 
        if (!target) LogInfo("SetTargetToHostName: Don't know how to set the target of rrtype %s", DNSTypeName(rr->resrec.rrtype));
 
-       if (!(rr->ForceMCast || rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->resrec.InterfaceID == mDNSInterface_P2P || IsLocalDomain(&rr->namestorage)))
+       if (!(rr->ForceMCast || rr->ARType == AuthRecordLocalOnly || rr->ARType == AuthRecordP2P || IsLocalDomain(&rr->namestorage)))
                {
                const domainname *const n = SetUnicastTargetToHostName(m, rr);
                if (n) newname = n;
@@ -648,6 +948,78 @@ mDNSexport void ActivateUnicastRegistration(mDNS *const m, AuthRecord *const rr)
 #define RecordIsLocalDuplicate(A,B) \
        ((A)->resrec.InterfaceID == (B)->resrec.InterfaceID && RecordLDT((A),(B)) && IdenticalResourceRecord(&(A)->resrec, &(B)->resrec))
 
+mDNSlocal AuthRecord *CheckAuthIdenticalRecord(AuthHash *r, AuthRecord *rr)
+       {
+       AuthGroup *a;
+       AuthGroup **ag = &a;
+       AuthRecord **rp;
+       const mDNSu32 slot = AuthHashSlot(rr->resrec.name);
+
+       a = AuthGroupForRecord(r, slot, &rr->resrec);
+       if (!a) return mDNSNULL;
+       rp = &(*ag)->members;
+       while (*rp)
+               {
+               if (!RecordIsLocalDuplicate(*rp, rr))
+                       rp=&(*rp)->next;
+               else
+                       {
+                       if ((*rp)->resrec.RecordType == kDNSRecordTypeDeregistering)
+                               {
+                               (*rp)->AnnounceCount = 0;
+                               rp=&(*rp)->next;
+                               }
+                       else return *rp;
+                       }
+               }
+       return (mDNSNULL);
+       }
+
+mDNSlocal mDNSBool CheckAuthRecordConflict(AuthHash *r, AuthRecord *rr)
+       {
+       AuthGroup *a;
+       AuthGroup **ag = &a;
+       AuthRecord **rp;
+       const mDNSu32 slot = AuthHashSlot(rr->resrec.name);
+
+       a = AuthGroupForRecord(r, slot, &rr->resrec);
+       if (!a) return mDNSfalse;
+       rp = &(*ag)->members;
+       while (*rp)
+               {
+               const AuthRecord *s1 = rr->RRSet ? rr->RRSet : rr;
+               const AuthRecord *s2 = (*rp)->RRSet ? (*rp)->RRSet : *rp;
+               if (s1 != s2 && SameResourceRecordSignature((*rp), rr) && !IdenticalSameNameRecord(&(*rp)->resrec, &rr->resrec))
+                       return mDNStrue;
+               else
+                       rp=&(*rp)->next;
+               }
+       return (mDNSfalse);
+       }
+
+// checks to see if "rr" is already present
+mDNSlocal AuthRecord *CheckAuthSameRecord(AuthHash *r, AuthRecord *rr)
+       {
+       AuthGroup *a;
+       AuthGroup **ag = &a;
+       AuthRecord **rp;
+       const mDNSu32 slot = AuthHashSlot(rr->resrec.name);
+
+       a = AuthGroupForRecord(r, slot, &rr->resrec);
+       if (!a) return mDNSNULL;
+       rp = &(*ag)->members;
+       while (*rp)
+               {
+               if (*rp != rr)
+                       rp=&(*rp)->next;
+               else
+                       {
+                       return *rp;
+                       }
+               }
+       return (mDNSNULL);
+       }
+
 // Exported so uDNS.c can call this
 mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr)
        {
@@ -668,22 +1040,45 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr)
        if (m->DivertMulticastAdvertisements && !AuthRecord_uDNS(rr))
                {
                mDNSInterfaceID previousID = rr->resrec.InterfaceID;
-               if (rr->resrec.InterfaceID == mDNSInterface_Any || rr->resrec.InterfaceID == mDNSInterface_P2P) rr->resrec.InterfaceID = mDNSInterface_LocalOnly;
+               if (rr->resrec.InterfaceID == mDNSInterface_Any || rr->resrec.InterfaceID == mDNSInterface_P2P)
+                       {
+                       rr->resrec.InterfaceID = mDNSInterface_LocalOnly;
+                       rr->ARType = AuthRecordLocalOnly;
+                       }
                if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly)
                        {
                        NetworkInterfaceInfo *intf = FirstInterfaceForID(m, rr->resrec.InterfaceID);
-                       if (intf && !intf->Advertise) rr->resrec.InterfaceID = mDNSInterface_LocalOnly;
+                       if (intf && !intf->Advertise){ rr->resrec.InterfaceID = mDNSInterface_LocalOnly; rr->ARType = AuthRecordLocalOnly; }
                        }
                if (rr->resrec.InterfaceID != previousID)
                        LogInfo("mDNS_Register_internal: Diverting record to local-only %s", ARDisplayString(m, rr));
                }
 
-       while (*p && *p != rr) p=&(*p)->next;
+       if (RRLocalOnly(rr))
+               {
+               if (CheckAuthSameRecord(&m->rrauth, rr))
+                       {
+                       LogMsg("mDNS_Register_internal: ERROR!! Tried to register LocalOnly AuthRecord %p %##s (%s) that's already in the list",
+                               rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+                       return(mStatus_AlreadyRegistered);
+                       }
+               }
+       else
+               {
+               while (*p && *p != rr) p=&(*p)->next;
+               if (*p)
+                       {
+                       LogMsg("mDNS_Register_internal: ERROR!! Tried to register AuthRecord %p %##s (%s) that's already in the list",
+                               rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+                       return(mStatus_AlreadyRegistered);
+                       }
+               }
+
        while (*d && *d != rr) d=&(*d)->next;
-       if (*d || *p)
+       if (*d)
                {
-               LogMsg("Error! Tried to register AuthRecord %p %##s (%s) that's already in the list",
-                       rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+               LogMsg("mDNS_Register_internal: ERROR!! Tried to register AuthRecord %p %##s (%s) that's already in the Duplicate list",
+                               rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
                return(mStatus_AlreadyRegistered);
                }
 
@@ -697,7 +1092,7 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr)
                                rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
                        return(mStatus_Invalid);
                        }
-               if (!(rr->DependentOn->resrec.RecordType & (kDNSRecordTypeUnique | kDNSRecordTypeVerified)))
+               if (!(rr->DependentOn->resrec.RecordType & (kDNSRecordTypeUnique | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique)))
                        {
                        LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn->RecordType bad type %X",
                                rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->DependentOn->resrec.RecordType);
@@ -705,8 +1100,10 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr)
                        }
                }
 
-       // If this resource record is referencing a specific interface, make sure it exists
-       if (rr->resrec.InterfaceID && rr->resrec.InterfaceID != mDNSInterface_LocalOnly && rr->resrec.InterfaceID != mDNSInterface_P2P)
+       // If this resource record is referencing a specific interface, make sure it exists.
+       // Skip checks for LocalOnly and P2P as they are not valid InterfaceIDs. Also, for scoped
+       // entries in /etc/hosts skip that check as that interface may not be valid at this time.
+       if (rr->resrec.InterfaceID && rr->ARType != AuthRecordLocalOnly && rr->ARType != AuthRecordP2P)
                {
                NetworkInterfaceInfo *intf = FirstInterfaceForID(m, rr->resrec.InterfaceID);
                if (!intf)
@@ -829,31 +1226,24 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr)
        rr->resrec.namehash   = DomainNameHashValue(rr->resrec.name);
        rr->resrec.rdatahash  = target ? DomainNameHashValue(target) : RDataHashValue(&rr->resrec);
        
-       if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->resrec.InterfaceID == mDNSInterface_P2P)
+       if (RRLocalOnly(rr))
                {
-               // If this is supposed to be unique, make sure we don't have any name conflicts
+               // If this is supposed to be unique, make sure we don't have any name conflicts.
+               // If we found a conflict, we may still want to insert the record in the list but mark it appropriately
+               // (kDNSRecordTypeDeregistering) so that we deliver RMV events to the application. But this causes more
+               // complications and not clear whether there are any benefits. See rdar:9304275 for details.
+               // Hence, just bail out.
                if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
                        {
-                       const AuthRecord *s1 = rr->RRSet ? rr->RRSet : rr;
-                       for (r = m->ResourceRecords; r; r=r->next)
+                       if (CheckAuthRecordConflict(&m->rrauth, rr))
                                {
-                               const AuthRecord *s2 = r->RRSet ? r->RRSet : r;
-                               if (s1 != s2 && SameResourceRecordSignature(r, rr) && !IdenticalSameNameRecord(&r->resrec, &rr->resrec))
-                                       break;
-                               }
-                       if (r)  // If we found a conflict, set RecordType = kDNSRecordTypeDeregistering so we'll deliver the callback
-                               {
-                               debugf("Name conflict %p %##s (%s)", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
-                               rr->resrec.RecordType    = kDNSRecordTypeDeregistering;
-                               rr->resrec.rroriginalttl = 0;
-                               rr->ImmedAnswer          = mDNSInterfaceMark;
-                               m->LocalRemoveEvents     = mDNStrue;
-                               m->NextScheduledResponse = m->timenow;
+                               LogInfo("mDNS_Register_internal: Name conflict %s (%p), InterfaceID %p", ARDisplayString(m, rr), rr, rr->resrec.InterfaceID);
+                               return mStatus_NameConflict;
                                }
                        }
                }
 
-       // For uDNS records, we don't support duplicate checks at this time
+       // For uDNS records, we don't support duplicate checks at this time.
 #ifndef UNICAST_DISABLED
        if (AuthRecord_uDNS(rr))
                {
@@ -872,13 +1262,22 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr)
 #endif
 
        // Now that we've finished building our new record, make sure it's not identical to one we already have
-       for (r = m->ResourceRecords; r; r=r->next)
-               if (RecordIsLocalDuplicate(r, rr))
-                       {
-                       if (r->resrec.RecordType == kDNSRecordTypeDeregistering) r->AnnounceCount = 0;
-                       else break;
-                       }
-       
+       if (RRLocalOnly(rr))
+               {
+               rr->ProbeCount    = 0;
+               rr->AnnounceCount = 0;
+               r = CheckAuthIdenticalRecord(&m->rrauth, rr);
+               }
+       else
+               {
+               for (r = m->ResourceRecords; r; r=r->next)
+                       if (RecordIsLocalDuplicate(r, rr))
+                               {
+                               if (r->resrec.RecordType == kDNSRecordTypeDeregistering) r->AnnounceCount = 0;
+                               else break;
+                               }
+               }
+
        if (r)
                {
                debugf("mDNS_Register_internal:Adding to duplicate list %s", ARDisplayString(m,rr));
@@ -893,8 +1292,24 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr)
        else
                {
                debugf("mDNS_Register_internal: Adding to active record list %s", ARDisplayString(m,rr));
-               if (!m->NewLocalRecords) m->NewLocalRecords = rr;
-               *p = rr;
+               if (RRLocalOnly(rr))
+                       {
+                       AuthGroup *ag;
+                       ag = InsertAuthRecord(m, &m->rrauth, rr);
+                       if (ag && !ag->NewLocalOnlyRecords) {
+                               m->NewLocalOnlyRecords = mDNStrue;
+                               ag->NewLocalOnlyRecords = rr;
+                       }
+                       // No probing for LocalOnly records, Acknowledge them right away
+                       if (rr->resrec.RecordType == kDNSRecordTypeUnique) rr->resrec.RecordType = kDNSRecordTypeVerified;
+                       AcknowledgeRecord(m, rr);
+                       return(mStatus_NoError);
+                       }
+               else
+                       {
+                       if (!m->NewLocalRecords) m->NewLocalRecords = rr;
+                       *p = rr;
+                       }
                }
 
        if (!AuthRecord_uDNS(rr))       // This check is superfluous, given that for unicast records we (currently) bail out above
@@ -947,8 +1362,25 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr,
        AuthRecord *r2;
        mDNSu8 RecordType = rr->resrec.RecordType;
        AuthRecord **p = &m->ResourceRecords;   // Find this record in our list of active records
+       mDNSBool dupList = mDNSfalse;
 
-       while (*p && *p != rr) p=&(*p)->next;
+       if (RRLocalOnly(rr))
+               {
+               AuthGroup *a;
+               AuthGroup **ag = &a;
+               AuthRecord **rp;
+               const mDNSu32 slot = AuthHashSlot(rr->resrec.name);
+       
+               a = AuthGroupForRecord(&m->rrauth, slot, &rr->resrec);
+               if (!a) return mDNSfalse;
+               rp = &(*ag)->members;
+               while (*rp && *rp != rr) rp=&(*rp)->next;
+               p = rp;
+               }
+       else
+               {
+               while (*p && *p != rr) p=&(*p)->next;
+               }
 
        if (*p)
                {
@@ -971,8 +1403,16 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr,
                                debugf("mDNS_Register_internal: Duplicate record %p taking over from %p %##s (%s)",
                                        dup, rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
                                *d        = dup->next;          // Cut replacement record from DuplicateRecords list
-                               dup->next = rr->next;           // And then...
-                               rr->next  = dup;                        // ... splice it in right after the record we're about to delete
+                               if (RRLocalOnly(rr))
+                                       {
+                                       dup->next = mDNSNULL;
+                                       if (!InsertAuthRecord(m, &m->rrauth, dup)) LogMsg("mDNS_Deregister_internal: ERROR!! cannot insert %s", ARDisplayString(m, dup));
+                                       }
+                               else
+                                       {
+                                       dup->next = rr->next;           // And then...
+                                       rr->next  = dup;                        // ... splice it in right after the record we're about to delete
+                                       }
                                dup->resrec.RecordType        = rr->resrec.RecordType;
                                dup->ProbeCount      = rr->ProbeCount;
                                dup->AnnounceCount   = rr->AnnounceCount;
@@ -1000,7 +1440,7 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr,
                p = &m->DuplicateRecords;
                while (*p && *p != rr) p=&(*p)->next;
                // If we found our record on the duplicate list, then make sure we don't send a goodbye for it
-               if (*p) rr->RequireGoodbye = mDNSfalse;
+               if (*p) { rr->RequireGoodbye = mDNSfalse; dupList = mDNStrue; }
                if (*p) debugf("mDNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)",
                        rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
                }
@@ -1093,10 +1533,18 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr,
                }
        else
                {
-               *p = rr->next;                                  // Cut this record from the list
+               if (!dupList && RRLocalOnly(rr))
+                       {
+                       AuthGroup *ag = RemoveAuthRecord(m, &m->rrauth, rr);
+                       if (ag->NewLocalOnlyRecords == rr) ag->NewLocalOnlyRecords = rr->next;
+                       }
+               else
+                       {
+                       *p = rr->next;                                  // Cut this record from the list
+                       if (m->NewLocalRecords == rr) m->NewLocalRecords = rr->next;
+                       }
                // If someone is about to look at this, bump the pointer forward
                if (m->CurrentRecord   == rr) m->CurrentRecord   = rr->next;
-               if (m->NewLocalRecords == rr) m->NewLocalRecords = rr->next;
                rr->next = mDNSNULL;
 
                // Should we generate local remove events here?
@@ -1638,6 +2086,7 @@ mDNSlocal void SendResponses(mDNS *const m)
                                                                SendNDP(m, NDP_Adv, NDP_Override, r2, &r2->AddressProxy.ip.v6, &r2->WakeUp.IMAC, &AllHosts_v6, &AllHosts_v6_Eth);
                                                                }
                                                        r2->LastAPTime = m->timenow;
+                                                       // After 15 wakeups without success (maybe host has left the network) send three goodbyes instead
                                                        if (--r2->AnnounceCount <= GoodbyeCount) r2->WakeUp.HMAC = zeroEthAddr;
                                                        }
                                        }
@@ -1785,7 +2234,16 @@ mDNSlocal void SendResponses(mDNS *const m)
                // 3. Answers and announcements we need to send
                for (rr = m->ResourceRecords; rr; rr=rr->next)
                        {
-                       if (rr->SendRNow == intf->InterfaceID)
+
+                       // Skip this interface if the record InterfaceID is *Any and the record is not 
+                       // appropriate for the interface type.
+                       if ((rr->SendRNow == intf->InterfaceID) &&
+                               ((rr->resrec.InterfaceID == mDNSInterface_Any) && !mDNSPlatformValidRecordForInterface(rr, intf)))
+                               {
+                                       LogInfo("SendResponses: Not Sending %s, on %s", ARDisplayString(m, rr), InterfaceNameForID(m, rr->SendRNow));
+                                       rr->SendRNow = GetNextActiveInterfaceID(intf);
+                               }
+                       else if (rr->SendRNow == intf->InterfaceID)
                                {
                                RData  *OldRData    = rr->resrec.rdata;
                                mDNSu16 oldrdlength = rr->resrec.rdlength;
@@ -1892,7 +2350,7 @@ mDNSlocal void SendResponses(mDNS *const m)
                        if (rr->SendNSECNow == mDNSInterfaceMark || rr->SendNSECNow == intf->InterfaceID)
                                {
                                AuthRecord nsec;
-                               mDNS_SetupResourceRecord(&nsec, mDNSNULL, mDNSInterface_Any, kDNSType_NSEC, rr->resrec.rroriginalttl, kDNSRecordTypeUnique, mDNSNULL, mDNSNULL);
+                               mDNS_SetupResourceRecord(&nsec, mDNSNULL, mDNSInterface_Any, kDNSType_NSEC, rr->resrec.rroriginalttl, kDNSRecordTypeUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
                                nsec.resrec.rrclass |= kDNSClass_UniqueRRSet;
                                AssignDomainName(&nsec.namestorage, rr->resrec.name);
                                mDNSPlatformMemZero(nsec.rdatastorage.u.nsec.bitmap, sizeof(nsec.rdatastorage.u.nsec.bitmap));
@@ -1929,7 +2387,7 @@ mDNSlocal void SendResponses(mDNS *const m)
                        if (OwnerRecordSpace)
                                {
                                AuthRecord opt;
-                               mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
+                               mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
                                opt.resrec.rrclass    = NormalMaxDNSMessageData;
                                opt.resrec.rdlength   = sizeof(rdataOPT);       // One option in this OPT record
                                opt.resrec.rdestimate = sizeof(rdataOPT);
@@ -1949,6 +2407,7 @@ mDNSlocal void SendResponses(mDNS *const m)
                                numAnnounce,              numAnnounce              == 1 ? "" : "s",
                                numAnswer,                numAnswer                == 1 ? "" : "s",
                                m->omsg.h.numAdditionals, m->omsg.h.numAdditionals == 1 ? "" : "s", intf->InterfaceID);
+
                        if (intf->IPv4Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, mDNSNULL, &AllDNSLinkGroup_v4, MulticastDNSPort, mDNSNULL, mDNSNULL);
                        if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, mDNSNULL, &AllDNSLinkGroup_v6, MulticastDNSPort, mDNSNULL, mDNSNULL);
                        if (!m->SuppressSending) m->SuppressSending = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+9)/10);
@@ -1981,7 +2440,7 @@ mDNSlocal void SendResponses(mDNS *const m)
 
                if (rr->SendRNow)
                        {
-                       if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly && rr->resrec.InterfaceID != mDNSInterface_P2P)
+                       if (rr->ARType != AuthRecordLocalOnly && rr->ARType != AuthRecordP2P)
                                LogMsg("SendResponses: No active interface %p to send: %p %02X %s", rr->SendRNow, rr->resrec.InterfaceID, rr->resrec.RecordType, ARDisplayString(m, rr));
                        rr->SendRNow = mDNSNULL;
                        }
@@ -2332,6 +2791,7 @@ mDNSlocal void mDNSSendWakeOnResolve(mDNS *const m, DNSQuestion *q)
                }
        LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed WakeOnResolve name %##s", q->qname.c);
        }
+               
 
 mDNSlocal mDNSBool AccelerateThisQuery(mDNS *const m, DNSQuestion *q)
        {
@@ -2701,7 +3161,7 @@ mDNSlocal void SendQueries(mDNS *const m)
                        if (OwnerRecordSpace)
                                {
                                AuthRecord opt;
-                               mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
+                               mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
                                opt.resrec.rrclass    = NormalMaxDNSMessageData;
                                opt.resrec.rdlength   = sizeof(rdataOPT);       // One option in this OPT record
                                opt.resrec.rdestimate = sizeof(rdataOPT);
@@ -2750,7 +3210,7 @@ mDNSlocal void SendQueries(mDNS *const m)
        for (ar = m->ResourceRecords; ar; ar=ar->next)
                if (ar->SendRNow)
                        {
-                       if (ar->resrec.InterfaceID != mDNSInterface_LocalOnly && ar->resrec.InterfaceID != mDNSInterface_P2P)
+                       if (ar->ARType != AuthRecordLocalOnly && ar->ARType != AuthRecordP2P)
                                LogMsg("SendQueries: No active interface %p to send probe: %p %s", ar->SendRNow, ar->resrec.InterfaceID, ARDisplayString(m, ar));
                        ar->SendRNow = mDNSNULL;
                        }
@@ -2832,12 +3292,31 @@ mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAdd
 mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheRecord *const rr, const QC_result AddRecord)
        {
        DNSQuestion *const q = m->CurrentQuestion;
-       mDNSBool followcname = rr->resrec.RecordType != kDNSRecordTypePacketNegative && AddRecord &&
-                                                       rr->resrec.rrtype == kDNSType_CNAME && q->qtype != kDNSType_CNAME;
+       mDNSBool followcname = FollowCNAME(q, &rr->resrec, AddRecord);
+
        verbosedebugf("AnswerCurrentQuestionWithResourceRecord:%4lu %s TTL %d %s",
                q->CurrentAnswers, AddRecord ? "Add" : "Rmv", rr->resrec.rroriginalttl, CRDisplayString(m, rr));
 
-       if (QuerySuppressed(q)) return;
+       // Normally we don't send out the unicast query if we have answered using our local only auth records e.g., /etc/hosts.
+       // But if the query for "A" record has a local answer but query for "AAAA" record has no local answer, we might
+       // send the AAAA query out which will come back with CNAME and will also answer the "A" query. To prevent that,
+       // we check to see if that query already has a unique local answer.
+       if (q->LOAddressAnswers)
+               {
+               LogInfo("AnswerCurrentQuestionWithResourceRecord: Question %p %##s (%s) not answering with record %s due to "
+                       "LOAddressAnswers %d", q, q->qname.c, DNSTypeName(q->qtype), ARDisplayString(m, rr),
+                       q->LOAddressAnswers);
+               return;
+               }
+
+       if (QuerySuppressed(q))
+               {
+               // If the query is suppressed, then we don't want to answer from the cache. But if this query is
+               // supposed to time out, we still want to callback the clients. We do this only for TimeoutQuestions
+               // that are timing out, which we know are answered with Negative cache record when timing out.
+               if (!q->TimeoutQuestion || rr->resrec.RecordType != kDNSRecordTypePacketNegative || (m->timenow - q->StopTime < 0))
+                       return;
+               }
 
        // Note: Use caution here. In the case of records with rr->DelayDelivery set, AnswerCurrentQuestionWithResourceRecord(... mDNStrue)
        // may be called twice, once when the record is received, and again when it's time to notify local clients.
@@ -2896,50 +3375,7 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco
        // including starting/stopping queries, registering/deregistering records, etc.
 
        if (followcname && m->CurrentQuestion == q)
-               {
-               const mDNSBool selfref = SameDomainName(&q->qname, &rr->resrec.rdata->u.name);
-               if (q->CNAMEReferrals >= 10 || selfref)
-                       LogMsg("AnswerCurrentQuestionWithResourceRecord: %p %##s (%s) NOT following CNAME referral %d%s for %s",
-                               q, q->qname.c, DNSTypeName(q->qtype), q->CNAMEReferrals, selfref ? " (Self-Referential)" : "", CRDisplayString(m, rr));
-               else
-                       {
-                       const mDNSu32 c = q->CNAMEReferrals + 1;                // Stash a copy of the new q->CNAMEReferrals value
-
-                       // The SameDomainName check above is to ignore bogus CNAME records that point right back at
-                       // themselves. Without that check we can get into a case where we have two duplicate questions,
-                       // A and B, and when we stop question A, UpdateQuestionDuplicates copies the value of CNAMEReferrals
-                       // from A to B, and then A is re-appended to the end of the list as a duplicate of B (because
-                       // the target name is still the same), and then when we stop question B, UpdateQuestionDuplicates
-                       // copies the B's value of CNAMEReferrals back to A, and we end up not incrementing CNAMEReferrals
-                       // for either of them. This is not a problem for CNAME loops of two or more records because in
-                       // those cases the newly re-appended question A has a different target name and therefore cannot be
-                       // a duplicate of any other question ('B') which was itself a duplicate of the previous question A.
-
-                       // Right now we just stop and re-use the existing query. If we really wanted to be 100% perfect,
-                       // and track CNAMEs coming and going, we should really create a subordinate query here,
-                       // which we would subsequently cancel and retract if the CNAME referral record were removed.
-                       // In reality this is such a corner case we'll ignore it until someone actually needs it.
-                       LogInfo("AnswerCurrentQuestionWithResourceRecord: %p %##s (%s) following CNAME referral %d for %s",
-                               q, q->qname.c, DNSTypeName(q->qtype), q->CNAMEReferrals, CRDisplayString(m, rr));
-
-                       mDNS_StopQuery_internal(m, q);                                                          // Stop old query
-                       AssignDomainName(&q->qname, &rr->resrec.rdata->u.name);         // Update qname
-                       q->qnamehash = DomainNameHashValue(&q->qname);                          // and namehash
-                       // If a unicast query results in a CNAME that points to a .local, we need to re-try
-                       // this as unicast. Setting the mDNSInterface_Unicast tells mDNS_StartQuery_internal
-                       // to try this as unicast query even though it is a .local name
-                       if (!mDNSOpaque16IsZero(q->TargetQID) && IsLocalDomain(&q->qname))
-                               {
-                               LogInfo("AnswerCurrentQuestionWithResourceRecord: Resolving a .local CNAME %p %##s (%s) CacheRecord %s",
-                                       q, q->qname.c, DNSTypeName(q->qtype), CRDisplayString(m, rr));
-                               q->InterfaceID = mDNSInterface_Unicast;
-                               }
-                       mDNS_StartQuery_internal(m, q);                                                         // start new query
-                       // Record how many times we've done this. We need to do this *after* mDNS_StartQuery_internal,
-                       // because mDNS_StartQuery_internal re-initializes CNAMEReferrals to zero
-                       q->CNAMEReferrals = c;
-                       }
-               }
+               AnswerQuestionByFollowingCNAME(m, q, &rr->resrec);
        }
 
 // New Questions are answered through AnswerNewQuestion. But there may not have been any
@@ -3333,25 +3769,15 @@ mDNSlocal void CheckCacheExpiration(mDNS *const m, const mDNSu32 slot, CacheGrou
        m->lock_rrcache = 0;
        }
 
-// Caller should hold the lock
-mDNSlocal void AnswerSuppressUnusableQuestion(mDNS *const m, DNSQuestion *q)
-       {
-       LogInfo("AnswerSuppressUnusableQuestion: Generating negative response for question %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
-       if (!m->CurrentQuestion) LogMsg("AnswerSuppressUnusableQuestion: ERROR!! CurrentQuestion not set");
-
-       MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, mDNSInterface_Any, mDNSNULL);
-       AnswerCurrentQuestionWithResourceRecord(m, &m->rec.r, QC_addnocache);
-       if (m->CurrentQuestion == q) q->ThisQInterval = 0;                              // Deactivate this question
-       // Don't touch the question after this
-       m->rec.r.resrec.RecordType = 0;         // Clear RecordType to show we're not still using it
-       }
-
 mDNSlocal void AnswerNewQuestion(mDNS *const m)
        {
        mDNSBool ShouldQueryImmediately = mDNStrue;
        DNSQuestion *const q = m->NewQuestions;         // Grab the question we're going to answer
-       const mDNSu32 slot = HashSlot(&q->qname);
+       mDNSu32 slot = HashSlot(&q->qname);
        CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
+       AuthRecord *lr;
+       AuthGroup *ag;
+       mDNSBool AnsweredFromCache = mDNSfalse;
 
        verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
 
@@ -3392,35 +3818,75 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m)
                MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, mDNSInterface_Any, q->qDNSServer);
                q->NoAnswer = NoAnswer_Normal;          // Temporarily turn off answer suppression
                AnswerCurrentQuestionWithResourceRecord(m, &m->rec.r, QC_addnocache);
-               q->NoAnswer = NoAnswer_Fail;            // Restore NoAnswer state
+               // Don't touch the question if it has been stopped already
+               if (m->CurrentQuestion == q) q->NoAnswer = NoAnswer_Fail;               // Restore NoAnswer state
                m->rec.r.resrec.RecordType = 0;         // Clear RecordType to show we're not still using it
                }
        if (m->CurrentQuestion != q) { LogInfo("AnswerNewQuestion: Question deleted while generating NoAnswer_Fail response"); goto exit; }
 
-       // If 'mDNSInterface_Any' question, see if we want to tell it about LocalOnly records
-       if (q->InterfaceID == mDNSInterface_Any)
+       // See if we want to tell it about LocalOnly records
+       if (m->CurrentRecord)
+               LogMsg("AnswerNewQuestion ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
+       slot = AuthHashSlot(&q->qname);
+       ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname);
+       if (ag)
                {
-               if (m->CurrentRecord)
-                       LogMsg("AnswerNewQuestion ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
-               m->CurrentRecord = m->ResourceRecords;
-               while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords)
+               m->CurrentRecord = ag->members;
+               while (m->CurrentRecord && m->CurrentRecord != ag->NewLocalOnlyRecords)
                        {
                        AuthRecord *rr = m->CurrentRecord;
                        m->CurrentRecord = rr->next;
-                       if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->resrec.InterfaceID == mDNSInterface_P2P)
-                               if (ResourceRecordAnswersQuestion(&rr->resrec, q))
+                       //
+                       // If the question is mDNSInterface_LocalOnly, all records local to the machine should be used
+                       // to answer the query. This is handled in AnswerNewLocalOnlyQuestion.
+                       //
+                       // We handle mDNSInterface_Any and scoped questions here. See LocalOnlyRecordAnswersQuestion for more
+                       // details on how we handle this case. For P2P we just handle "Interface_Any" questions. For LocalOnly
+                       // we handle both mDNSInterface_Any and scoped questions.
+
+                       if (rr->ARType == AuthRecordLocalOnly || (rr->ARType == AuthRecordP2P && q->InterfaceID == mDNSInterface_Any))
+                               if (LocalOnlyRecordAnswersQuestion(rr, q))
                                        {
-                                       AnswerLocalQuestionWithLocalAuthRecord(m, q, rr, mDNStrue);
+                                       AnswerLocalQuestionWithLocalAuthRecord(m, rr, mDNStrue);
                                        if (m->CurrentQuestion != q) break;             // If callback deleted q, then we're finished here
                                        }
                        }
-               m->CurrentRecord = mDNSNULL;
                }
+       m->CurrentRecord = mDNSNULL;
+       
        if (m->CurrentQuestion != q) { LogInfo("AnswerNewQuestion: Question deleted while while giving LocalOnly record answers"); goto exit; }
 
+       if (q->LOAddressAnswers)
+               {
+               LogInfo("AnswerNewQuestion: Question %p %##s (%s) answered using local auth records LOAddressAnswers %d",
+                       q, q->qname.c, DNSTypeName(q->qtype), q->LOAddressAnswers);
+               goto exit;
+               }
+
+       // Before we go check the cache and ship this query on the wire, we have to be sure that there are
+       // no local records that could possibly answer this question. As we did not check the NewLocalRecords, we
+       // need to just peek at them to see whether it will answer this question. If it would answer, pretend
+       // that we answered. AnswerAllLocalQuestionsWithLocalAuthRecord will answer shortly. This happens normally
+       // when we add new /etc/hosts entries and restart the question. It is a new question and also a new record.
+       if (ag)
+               {
+               lr = ag->NewLocalOnlyRecords;
+               while (lr)
+                       {
+                       if (LORecordAnswersAddressType(lr) && LocalOnlyRecordAnswersQuestion(lr, q))
+                               {
+                               LogInfo("AnswerNewQuestion: Question %p %##s (%s) will be answered using new local auth records "
+                                       " LOAddressAnswers %d", q, q->qname.c, DNSTypeName(q->qtype), q->LOAddressAnswers);
+                               goto exit;
+                               }
+                       lr = lr->next;
+                       }
+               }
+       
+
        // If we are not supposed to answer this question, generate a negative response.
        // Temporarily suspend the SuppressQuery so that AnswerCurrentQuestionWithResourceRecord can answer the question
-       if (QuerySuppressed(q)) { q->SuppressQuery = mDNSfalse; AnswerSuppressUnusableQuestion(m, q); q->SuppressQuery = mDNStrue; }
+       if (QuerySuppressed(q)) { q->SuppressQuery = mDNSfalse; GenerateNegativeResponse(m); q->SuppressQuery = mDNStrue; }
        else
                {
                CacheRecord *rr;
@@ -3443,6 +3909,7 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m)
                                q->CurrentAnswers++;
                                if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++;
                                if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++;
+                               AnsweredFromCache = mDNStrue;
                                AnswerCurrentQuestionWithResourceRecord(m, rr, QC_add);
                                if (m->CurrentQuestion != q) break;             // If callback deleted q, then we're finished here
                                }
@@ -3453,6 +3920,20 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m)
        // it's not remotely remarkable, and therefore unlikely to be of much help tracking down bugs.
        if (m->CurrentQuestion != q) { debugf("AnswerNewQuestion: Question deleted while giving cache answers"); goto exit; }
 
+       // Neither a local record nor a cache entry could answer this question. If this question need to be retried
+       // with search domains, generate a negative response which will now retry after appending search domains.
+       // If the query was suppressed above, we already generated a negative response. When it gets unsuppressed,
+       // we will retry with search domains.
+       if (!QuerySuppressed(q) && !AnsweredFromCache && q->RetryWithSearchDomains)
+               {
+               LogInfo("AnswerNewQuestion: Generating response for retrying with search domains %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+               GenerateNegativeResponse(m);
+               }
+
+       if (m->CurrentQuestion != q) { debugf("AnswerNewQuestion: Question deleted while giving negative answer"); goto exit; }
+
+       // Note: When a query gets suppressed or retried with search domains, we de-activate the question.
+       // Hence we don't execute the following block of code for those cases.
        if (ShouldQueryImmediately && ActiveQuestion(q))
                {
                debugf("AnswerNewQuestion: ShouldQueryImmediately %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
@@ -3480,9 +3961,11 @@ exit:
        }
 
 // When a NewLocalOnlyQuestion is created, AnswerNewLocalOnlyQuestion runs though our ResourceRecords delivering any
-// appropriate answers, stopping if it reaches a NewLocalRecord -- these will be handled by AnswerAllLocalQuestionsWithLocalAuthRecord
+// appropriate answers, stopping if it reaches a NewLocalOnlyRecord -- these will be handled by AnswerAllLocalQuestionsWithLocalAuthRecord
 mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m)
        {
+       mDNSu32 slot;
+       AuthGroup *ag;
        DNSQuestion *q = m->NewLocalOnlyQuestions;              // Grab the question we're going to answer
        m->NewLocalOnlyQuestions = q->next;                             // Advance NewLocalOnlyQuestions to the next (if any)
 
@@ -3495,16 +3978,40 @@ mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m)
 
        if (m->CurrentRecord)
                LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
-       m->CurrentRecord = m->ResourceRecords;
 
-       while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords)
+       // 1. First walk the LocalOnly records answering the LocalOnly question
+       // 2. As LocalOnly questions should also be answered by any other Auth records local to the machine,
+       //    walk the ResourceRecords list delivering the answers
+       slot = AuthHashSlot(&q->qname);
+       ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname);
+       if (ag)
                {
-               AuthRecord *rr = m->CurrentRecord;
-               m->CurrentRecord = rr->next;
-               if (ResourceRecordAnswersQuestion(&rr->resrec, q))
+               m->CurrentRecord = ag->members;
+               while (m->CurrentRecord && m->CurrentRecord != ag->NewLocalOnlyRecords)
                        {
-                       AnswerLocalQuestionWithLocalAuthRecord(m, q, rr, mDNStrue);
-                       if (m->CurrentQuestion != q) break;             // If callback deleted q, then we're finished here
+                       AuthRecord *rr = m->CurrentRecord;
+                       m->CurrentRecord = rr->next;
+                       if (LocalOnlyRecordAnswersQuestion(rr, q))
+                               {
+                               AnswerLocalQuestionWithLocalAuthRecord(m, rr, mDNStrue);
+                               if (m->CurrentQuestion != q) break;             // If callback deleted q, then we're finished here
+                               }
+                       }
+               }
+
+       if (m->CurrentQuestion == q)
+               {
+               m->CurrentRecord = m->ResourceRecords;
+
+               while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords)
+                       {
+                       AuthRecord *rr = m->CurrentRecord;
+                       m->CurrentRecord = rr->next;
+                       if (ResourceRecordAnswersQuestion(&rr->resrec, q))
+                               {
+                               AnswerLocalQuestionWithLocalAuthRecord(m, rr, mDNStrue);
+                               if (m->CurrentQuestion != q) break;             // If callback deleted q, then we're finished here
+                               }
                        }
                }
 
@@ -3695,20 +4202,77 @@ mDNSlocal void CheckProxyRecords(mDNS *const m, AuthRecord *list)
                                if (m->NextScheduledSPS - rr->TimeExpire > 0)
                                        m->NextScheduledSPS = rr->TimeExpire;
                                }
-                       else                                                                                                    // else proxy record expired, so remove it
+                       else                                                                                                    // else proxy record expired, so remove it
+                               {
+                               LogSPS("CheckProxyRecords: Removing %d H-MAC %.6a I-MAC %.6a %d %s",
+                                       m->ProxyRecords, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, ARDisplayString(m, rr));
+                               SetSPSProxyListChanged(rr->resrec.InterfaceID);
+                               mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
+                               // Don't touch rr after this -- memory may have been free'd
+                               }
+                       }
+               // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because
+               // new records could have been added to the end of the list as a result of that call.
+               if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now
+                       m->CurrentRecord = rr->next;
+               }
+       }
+
+mDNSlocal void CheckRmvEventsForLocalRecords(mDNS *const m)
+       {
+       while (m->CurrentRecord)
+               {
+               AuthRecord *rr = m->CurrentRecord;
+               if (rr->AnsweredLocalQ && rr->resrec.RecordType == kDNSRecordTypeDeregistering)
+                       {
+                       debugf("CheckRmvEventsForLocalRecords: Generating local RMV events for %s", ARDisplayString(m, rr));
+                       rr->resrec.RecordType = kDNSRecordTypeShared;
+                       AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNSfalse);
+                       if (m->CurrentRecord == rr)     // If rr still exists in list, restore its state now
+                               {
+                               rr->resrec.RecordType = kDNSRecordTypeDeregistering;
+                               rr->AnsweredLocalQ = mDNSfalse;
+                               // SendResponses normally calls CompleteDeregistration after sending goodbyes.
+                               // For LocalOnly records, we don't do that and hence we need to do that here.
+                               if (RRLocalOnly(rr)) CompleteDeregistration(m, rr);
+                               }
+                       }
+               if (m->CurrentRecord == rr)             // If m->CurrentRecord was not auto-advanced, do it ourselves now
+                       m->CurrentRecord = rr->next;
+               }
+       }
+
+mDNSlocal void TimeoutQuestions(mDNS *const m)
+       {
+       m->NextScheduledStopTime = m->timenow + 0x3FFFFFFF;
+       if (m->CurrentQuestion)
+               LogMsg("TimeoutQuestions ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c,
+                       DNSTypeName(m->CurrentQuestion->qtype));
+       m->CurrentQuestion = m->Questions;
+       while (m->CurrentQuestion)
+               {
+               DNSQuestion *const q = m->CurrentQuestion;
+               if (q->StopTime)
+                       {
+                       if (m->timenow - q->StopTime >= 0)
+                               {
+                               LogInfo("TimeoutQuestions: question %##s timed out, time %d", q->qname.c, m->timenow - q->StopTime);
+                               GenerateNegativeResponse(m);
+                               if (m->CurrentQuestion == q) q->StopTime = 0;
+                               }
+                       else
                                {
-                               LogSPS("CheckProxyRecords: Removing %d H-MAC %.6a I-MAC %.6a %d %s",
-                                       m->ProxyRecords, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, ARDisplayString(m, rr));
-                               SetSPSProxyListChanged(rr->resrec.InterfaceID);
-                               mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
-                               // Don't touch rr after this -- memory may have been free'd
+                               if (m->NextScheduledStopTime - q->StopTime > 0)
+                                       m->NextScheduledStopTime = q->StopTime;
                                }
                        }
-               // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because
-               // new records could have been added to the end of the list as a result of that call.
-               if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now
-                       m->CurrentRecord = rr->next;
+               // If m->CurrentQuestion wasn't modified out from under us, advance it now
+               // We can't do this at the start of the loop because GenerateNegativeResponse
+               // depends on having m->CurrentQuestion point to the right question
+               if (m->CurrentQuestion == q)
+                       m->CurrentQuestion = q->next;
                }
+       m->CurrentQuestion = mDNSNULL;
        }
 
 mDNSexport mDNSs32 mDNS_Execute(mDNS *const m)
@@ -3719,6 +4283,8 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m)
                {
                int i;
                AuthRecord *head, *tail;
+               mDNSu32 slot;
+               AuthGroup *ag;
 
                verbosedebugf("mDNS_Execute");
 
@@ -3738,7 +4304,7 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m)
                // 3. Purge our cache of stale old records
                if (m->rrcache_size && m->timenow - m->NextCacheCheck >= 0)
                        {
-                       mDNSu32 slot, numchecked = 0;
+                       mDNSu32 numchecked = 0;
                        m->NextCacheCheck = m->timenow + 0x3FFFFFFF;
                        for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
                                {
@@ -3799,26 +4365,18 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m)
                        {
                        m->LocalRemoveEvents = mDNSfalse;
                        m->CurrentRecord = m->ResourceRecords;
-                       while (m->CurrentRecord)
-                               {
-                               AuthRecord *rr = m->CurrentRecord;
-                               if (rr->AnsweredLocalQ && rr->resrec.RecordType == kDNSRecordTypeDeregistering)
+                       CheckRmvEventsForLocalRecords(m);
+                       // Walk the LocalOnly records and deliver the RMV events
+                       for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+                               for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next)
                                        {
-                                       debugf("mDNS_Execute: Generating local RMV events for %s", ARDisplayString(m, rr));
-                                       rr->resrec.RecordType = kDNSRecordTypeShared;
-                                       AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNSfalse);
-                                       if (m->CurrentRecord == rr)     // If rr still exists in list, restore its state now
-                                               {
-                                               rr->resrec.RecordType = kDNSRecordTypeDeregistering;
-                                               rr->AnsweredLocalQ = mDNSfalse;
-                                               }
+                                       m->CurrentRecord = ag->members;
+                                       if (m->CurrentRecord) CheckRmvEventsForLocalRecords(m);
                                        }
-                               if (m->CurrentRecord == rr)             // If m->CurrentRecord was not auto-advanced, do it ourselves now
-                                       m->CurrentRecord = rr->next;
-                               }
                        }
+
                if (i >= 1000) LogMsg("mDNS_Execute: m->LocalRemoveEvents exceeded loop limit");
-               
+                               
                for (i=0; m->NewLocalOnlyQuestions && i<1000; i++) AnswerNewLocalOnlyQuestion(m);
                if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewLocalOnlyQuestion exceeded loop limit");
 
@@ -3871,6 +4429,31 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m)
 
                if (i >= 1000) LogMsg("mDNS_Execute: m->NewLocalRecords exceeded loop limit");
 
+               // Check to see if we have any new LocalOnly/P2P records to examine for delivering
+               // to our local questions
+               if (m->NewLocalOnlyRecords)
+                       {
+                       m->NewLocalOnlyRecords = mDNSfalse;
+                       for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+                               for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next)
+                                       {
+                                       for (i=0; i<100 && ag->NewLocalOnlyRecords; i++)
+                                               {
+                                               AuthRecord *rr = ag->NewLocalOnlyRecords;
+                                               ag->NewLocalOnlyRecords = ag->NewLocalOnlyRecords->next;
+                                               // LocalOnly records should always be ready as they never probe
+                                               if (LocalRecordReady(rr))
+                                                       {
+                                                       debugf("mDNS_Execute: Delivering Add event with LocalAuthRecord %s", ARDisplayString(m, rr));
+                                                       AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNStrue);
+                                                       }
+                                               else LogMsg("mDNS_Execute: LocalOnlyRecord %s not ready", ARDisplayString(m, rr));
+                                               }
+                                       // We limit about 100 per AuthGroup that can be serviced at a time
+                                       if (i >= 100) LogMsg("mDNS_Execute: ag->NewLocalOnlyRecords exceeded loop limit");
+                                       }
+                       }
+
                // 5. Some questions may have picked a new DNS server and the cache may answer these questions now.
                AnswerQuestionsForDNSServerChanges(m);
 
@@ -3919,6 +4502,7 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m)
                m->RandomQueryDelay     = 0;
                m->RandomReconfirmDelay = 0;
 
+               if (m->NextScheduledStopTime && m->timenow - m->NextScheduledStopTime >= 0) TimeoutQuestions(m);
 #ifndef UNICAST_DISABLED
                if (m->NextSRVUpdate && m->timenow - m->NextSRVUpdate >= 0) UpdateAllSRVRecords(m);
                if (m->timenow - m->NextScheduledNATOp >= 0) CheckNATMappings(m);
@@ -3958,6 +4542,27 @@ mDNSlocal void SuspendLLQs(mDNS *m)
                        { q->ReqLease = 0; sendLLQRefresh(m, q); }
        }
 
+mDNSlocal mDNSBool QuestionHasLocalAnswers(mDNS *const m, DNSQuestion *q)
+       {
+       AuthRecord *rr;
+       mDNSu32 slot;
+       AuthGroup *ag;
+
+       slot = AuthHashSlot(&q->qname);
+       ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname);
+       if (ag)
+               {
+               for (rr = ag->members; rr; rr=rr->next)
+                       // Filter the /etc/hosts records - LocalOnly, Unique, A/AAAA/CNAME
+                       if (LORecordAnswersAddressType(rr) && LocalOnlyRecordAnswersQuestion(rr, q))
+                               {
+                               LogInfo("QuestionHasLocalAnswers: Question %p %##s (%s) has local answer %s", q, q->qname.c, DNSTypeName(q->qtype), ARDisplayString(m, rr));
+                               return mDNStrue;
+                               }
+               }
+       return mDNSfalse;
+       }
+
 // ActivateUnicastQuery() is called from three places:
 // 1. When a new question is created
 // 2. On wake from sleep
@@ -3998,7 +4603,8 @@ mDNSlocal void ActivateUnicastQuery(mDNS *const m, DNSQuestion *const question,
                        question->servPort = zeroIPPort;
                        if (question->tcp) { DisposeTCPConn(question->tcp); question->tcp = mDNSNULL; }
                        }
-               if (ScheduleImmediately)
+               // If the question has local answers, then we don't want answers from outside
+               if (ScheduleImmediately && !QuestionHasLocalAnswers(m, question))
                        {
                        question->ThisQInterval = InitialQuestionInterval;
                        question->LastQTime     = m->timenow - question->ThisQInterval;
@@ -4007,6 +4613,115 @@ mDNSlocal void ActivateUnicastQuery(mDNS *const m, DNSQuestion *const question,
                }
        }
 
+// Caller should hold the lock
+mDNSexport void mDNSCoreRestartAddressQueries(mDNS *const m, mDNSBool SearchDomainsChanged, FlushCache flushCacheRecords,
+       CallbackBeforeStartQuery BeforeStartCallback, void *context)
+       {
+       DNSQuestion *q;
+       DNSQuestion *restart = mDNSNULL;
+
+       if (!m->mDNS_busy) LogMsg("mDNSCoreRestartAddressQueries: ERROR!! Lock not held");
+
+       // 1. Flush the cache records
+       if (flushCacheRecords) flushCacheRecords(m);
+
+       // 2. Even though we may have purged the cache records above, before it can generate RMV event
+       // we are going to stop the question. Hence we need to deliver the RMV event before we
+       // stop the question.
+       //
+       // CurrentQuestion is used by RmvEventsForQuestion below. While delivering RMV events, the
+       // application callback can potentially stop the current question (detected by CurrentQuestion) or
+       // *any* other question which could be the next one that we may process here. RestartQuestion
+       // points to the "next" question which will be automatically advanced in mDNS_StopQuery_internal
+       // if the "next" question is stopped while the CurrentQuestion is stopped
+
+       if (m->RestartQuestion)
+               LogMsg("mDNSCoreRestartAddressQueries: ERROR!! m->RestartQuestion already set: %##s (%s)",
+                       m->RestartQuestion->qname.c, DNSTypeName(m->RestartQuestion->qtype));
+
+       m->RestartQuestion = m->Questions;
+       while (m->RestartQuestion)
+               {
+               q = m->RestartQuestion;
+               m->RestartQuestion = q->next;
+               // GetZoneData questions are referenced by other questions (original query that started the GetZoneData
+               // question)  through their "nta" pointer. Normally when the original query stops, it stops the
+               // GetZoneData question and also frees the memory (See CancelGetZoneData). If we stop the GetZoneData
+               // question followed by the original query that refers to this GetZoneData question, we will end up
+               // freeing the GetZoneData question and then start the "freed" question at the end. 
+
+               if (IsGetZoneDataQuestion(q))
+                       {
+                       DNSQuestion *refq = q->next;
+                       LogInfo("mDNSCoreRestartAddressQueries: Skipping GetZoneDataQuestion %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype));
+                       // debug stuff, we just try to find the referencing question and don't do much with it
+                       while (refq)
+                               {
+                               if (q == &refq->nta->question)
+                                       {
+                                       LogInfo("mDNSCoreRestartAddressQueries: Question %p %##s (%s) referring to GetZoneDataQuestion %p, not stopping", refq, refq->qname.c, DNSTypeName(refq->qtype), q);
+                                       }
+                               refq = refq->next;
+                               }
+                       continue;
+                       }
+
+               // This function is called when /etc/hosts changes and that could affect A, AAAA and CNAME queries
+               if (q->qtype != kDNSType_A && q->qtype != kDNSType_AAAA && q->qtype != kDNSType_CNAME) continue;
+
+               // If the search domains did not change, then we restart all the queries. Otherwise, only
+               // for queries for which we "might" have appended search domains ("might" because we may
+               // find results before we apply search domains even though AppendSearchDomains is set to 1)
+               if (!SearchDomainsChanged || q->AppendSearchDomains)
+                       {
+                       // NOTE: CacheRecordRmvEventsForQuestion will not generate RMV events for queries that have non-zero
+                       // LOAddressAnswers. Hence it is important that we call CacheRecordRmvEventsForQuestion before
+                       // LocalRecordRmvEventsForQuestion (which decrements LOAddressAnswers). Let us say that
+                       // /etc/hosts has an A Record for web.apple.com. Any queries for web.apple.com will be answered locally.
+                       // But this can't prevent a CNAME/AAAA query to not to be sent on the wire. When it is sent on the wire,
+                       // it could create cache entries. When we are restarting queries, we can't deliver the cache RMV events
+                       // for the original query using these cache entries as ADDs were never delivered using these cache
+                       // entries and hence this order is needed.
+
+                       // If the query is suppressed, the RMV events won't be delivered
+                       if (!CacheRecordRmvEventsForQuestion(m, q)) { LogInfo("mDNSCoreRestartAddressQueries: Question deleted while delivering Cache Record RMV events"); continue; }
+
+                       // SuppressQuery status does not affect questions that are answered using local records
+                       if (!LocalRecordRmvEventsForQuestion(m, q)) { LogInfo("mDNSCoreRestartAddressQueries: Question deleted while delivering Local Record RMV events"); continue; }
+
+                       LogInfo("mDNSCoreRestartAddressQueries: Stop question %p %##s (%s), AppendSearchDomains %d, qnameOrig %p", q,
+                               q->qname.c, DNSTypeName(q->qtype), q->AppendSearchDomains, q->qnameOrig);
+                       mDNS_StopQuery_internal(m, q);
+                       // Reset state so that it looks like it was in the beginning i.e it should look at /etc/hosts, cache
+                       // and then search domains should be appended. At the beginning, qnameOrig was NULL.
+                       if (q->qnameOrig)
+                               {
+                               LogInfo("mDNSCoreRestartAddressQueries: qnameOrig %##s", q->qnameOrig);
+                               AssignDomainName(&q->qname, q->qnameOrig);
+                               mDNSPlatformMemFree(q->qnameOrig);
+                               q->qnameOrig = mDNSNULL;
+                               q->RetryWithSearchDomains = ApplySearchDomainsFirst(q) ? 1 : 0;
+                               }
+                       q->SearchListIndex = 0;
+                       q->next = restart;
+                       restart = q;
+                       }
+               }
+
+       // 3. Callback before we start the query
+       if (BeforeStartCallback) BeforeStartCallback(m, context);
+
+       // 4. Restart all the stopped queries
+       while (restart)
+               {
+               q = restart;
+               restart = restart->next;
+               q->next = mDNSNULL;
+               LogInfo("mDNSCoreRestartAddressQueries: Start question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype));
+               mDNS_StartQuery_internal(m, q);
+               }
+       }
+
 mDNSexport void mDNSCoreRestartQueries(mDNS *const m)
        {
        DNSQuestion *q;
@@ -4048,6 +4763,9 @@ mDNSexport void mDNS_UpdateAllowSleep(mDNS *const m)
        {
 #ifndef IDLESLEEPCONTROL_DISABLED
        mDNSBool allowSleep = mDNStrue;
+       char     reason[128];
+       
+       reason[0] = 0;
        
        if (m->SystemSleepOnlyIfWakeOnLAN)
                {
@@ -4055,6 +4773,7 @@ mDNSexport void mDNS_UpdateAllowSleep(mDNS *const m)
                if (m->ProxyRecords)
                        {
                        allowSleep = mDNSfalse;
+                       mDNS_snprintf(reason, sizeof(reason), "sleep proxy for %d records", m->ProxyRecords);
                        LogInfo("Sleep disabled because we are proxying %d records", m->ProxyRecords);
                        }
 
@@ -4064,12 +4783,13 @@ mDNSexport void mDNS_UpdateAllowSleep(mDNS *const m)
                        NetworkInterfaceInfo *intf;
                        for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next))
                                {
-                               if (intf->McastTxRx)
+                               if (intf->McastTxRx && !intf->Loopback)
                                        {
                                        // Disallow sleep if this interface doesn't support NetWake
                                        if (!intf->NetWake)
                                                {
                                                allowSleep = mDNSfalse;
+                                               mDNS_snprintf(reason, sizeof(reason), "%s does not support NetWake", intf->ifname);
                                                LogInfo("Sleep disabled because %s does not support NetWake", intf->ifname);
                                                break;
                                                }
@@ -4078,20 +4798,21 @@ mDNSexport void mDNS_UpdateAllowSleep(mDNS *const m)
                                        if (FindSPSInCache1(m, &intf->NetWakeBrowse, mDNSNULL, mDNSNULL) == mDNSNULL)
                                                {
                                                allowSleep = mDNSfalse;
+                                               mDNS_snprintf(reason, sizeof(reason), "%s does not support NetWake", intf->ifname);
                                                LogInfo("Sleep disabled because %s has no sleep proxy", intf->ifname);
                                                break;
                                                }
                                        }
                                }
                        }
-#endif /* !defined(IDLESLEEPCONTROL_DISABLED) */
                }
        
        // Call the platform code to enable/disable sleep
-       mDNSPlatformSetAllowSleep(m, allowSleep);
+       mDNSPlatformSetAllowSleep(m, allowSleep, reason);
+#endif /* !defined(IDLESLEEPCONTROL_DISABLED) */
        }
 
-mDNSlocal void SendSPSRegistration(mDNS *const m, NetworkInterfaceInfo *intf, const mDNSOpaque16 id)
+mDNSlocal void SendSPSRegistrationForOwner(mDNS *const m, NetworkInterfaceInfo *const intf, const mDNSOpaque16 id, const OwnerOptData *const owner)
        {
        const int optspace = DNSOpt_Header_Space + DNSOpt_LeaseData_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC);
        const int sps = intf->NextSPSAttempt / 3;
@@ -4111,7 +4832,8 @@ mDNSlocal void SendSPSRegistration(mDNS *const m, NetworkInterfaceInfo *intf, co
                for (rr = m->ResourceRecords; rr; rr=rr->next)
                        if (rr->resrec.RecordType > kDNSRecordTypeDeregistering)
                                if (rr->resrec.InterfaceID == intf->InterfaceID || (!rr->resrec.InterfaceID && (rr->ForceMCast || IsLocalDomain(rr->resrec.name))))
-                                       rr->SendRNow = mDNSInterfaceMark;       // mark it now
+                                       if (mDNSPlatformMemSame(owner, &rr->WakeUp, sizeof(*owner)))
+                                               rr->SendRNow = mDNSInterfaceMark;       // mark it now
 
        while (1)
                {
@@ -4124,40 +4846,48 @@ mDNSlocal void SendSPSRegistration(mDNS *const m, NetworkInterfaceInfo *intf, co
 
                for (rr = m->ResourceRecords; rr; rr=rr->next)
                        if (rr->SendRNow || (!mDNSOpaque16IsZero(id) && !AuthRecord_uDNS(rr) && mDNSSameOpaque16(rr->updateid, id) && m->timenow - (rr->LastAPTime + rr->ThisAPInterval) >= 0))
-                               {
-                               mDNSu8 *newptr;
-                               const mDNSu8 *const limit = m->omsg.data + (m->omsg.h.mDNS_numUpdates ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData) - optspace;
-                               if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
-                                       rr->resrec.rrclass |= kDNSClass_UniqueRRSet;    // Temporarily set the 'unique' bit so PutResourceRecord will set it
-                               newptr = PutResourceRecordTTLWithLimit(&m->omsg, p, &m->omsg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl, limit);
-                               rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet;           // Make sure to clear 'unique' bit back to normal state
-                               if (!newptr)
-                                       LogSPS("SendSPSRegistration put %s FAILED %d/%d %s", intf->ifname, p - m->omsg.data, limit - m->omsg.data, ARDisplayString(m, rr));
-                               else
+                               if (mDNSPlatformMemSame(owner, &rr->WakeUp, sizeof(*owner)))
                                        {
-                                       LogSPS("SendSPSRegistration put %s %s", intf->ifname, ARDisplayString(m, rr));
-                                       rr->SendRNow       = mDNSNULL;
-                                       rr->ThisAPInterval = mDNSPlatformOneSecond;
-                                       rr->LastAPTime     = m->timenow;
-                                       rr->updateid       = m->omsg.h.id;
-                                       if (m->NextScheduledResponse - (rr->LastAPTime + rr->ThisAPInterval) >= 0)
-                                               m->NextScheduledResponse = (rr->LastAPTime + rr->ThisAPInterval);
-                                       p = newptr;
+                                       mDNSu8 *newptr;
+                                       const mDNSu8 *const limit = m->omsg.data + (m->omsg.h.mDNS_numUpdates ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData) - optspace;
+                                       if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
+                                               rr->resrec.rrclass |= kDNSClass_UniqueRRSet;    // Temporarily set the 'unique' bit so PutResourceRecord will set it
+                                       newptr = PutResourceRecordTTLWithLimit(&m->omsg, p, &m->omsg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl, limit);
+                                       rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet;           // Make sure to clear 'unique' bit back to normal state
+                                       if (!newptr)
+                                               LogSPS("SendSPSRegistration put %s FAILED %d/%d %s", intf->ifname, p - m->omsg.data, limit - m->omsg.data, ARDisplayString(m, rr));
+                                       else
+                                               {
+                                               LogSPS("SendSPSRegistration put %s %s", intf->ifname, ARDisplayString(m, rr));
+                                               rr->SendRNow       = mDNSNULL;
+                                               rr->ThisAPInterval = mDNSPlatformOneSecond;
+                                               rr->LastAPTime     = m->timenow;
+                                               rr->updateid       = m->omsg.h.id;
+                                               if (m->NextScheduledResponse - (rr->LastAPTime + rr->ThisAPInterval) >= 0)
+                                                       m->NextScheduledResponse = (rr->LastAPTime + rr->ThisAPInterval);
+                                               p = newptr;
+                                               }
                                        }
-                               }
 
                if (!m->omsg.h.mDNS_numUpdates) break;
                else
                        {
                        AuthRecord opt;
-                       mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
+                       mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
                        opt.resrec.rrclass    = NormalMaxDNSMessageData;
                        opt.resrec.rdlength   = sizeof(rdataOPT) * 2;   // Two options in this OPT record
                        opt.resrec.rdestimate = sizeof(rdataOPT) * 2;
-                       opt.resrec.rdata->u.opt[0].opt              = kDNSOpt_Lease;
-                       opt.resrec.rdata->u.opt[0].optlen           = DNSOpt_LeaseData_Space - 4;
-                       opt.resrec.rdata->u.opt[0].u.updatelease    = DEFAULT_UPDATE_LEASE;
-                       SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[1]);
+                       opt.resrec.rdata->u.opt[0].opt           = kDNSOpt_Lease;
+                       opt.resrec.rdata->u.opt[0].optlen        = DNSOpt_LeaseData_Space - 4;
+                       opt.resrec.rdata->u.opt[0].u.updatelease = DEFAULT_UPDATE_LEASE;
+                       if (!owner->HMAC.l[0])                                                                                  // If no owner data,
+                               SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[1]);            // use our own interface information
+                       else                                                                                                                    // otherwise, use the owner data we were given
+                               {
+                               opt.resrec.rdata->u.opt[1].u.owner = *owner;
+                               opt.resrec.rdata->u.opt[1].opt     = kDNSOpt_Owner;
+                               opt.resrec.rdata->u.opt[1].optlen  = DNSOpt_Owner_Space(&owner->HMAC, &owner->IMAC) - 4;
+                               }
                        LogSPS("SendSPSRegistration put %s %s", intf->ifname, ARDisplayString(m, &opt));
                        p = PutResourceRecordTTLWithLimit(&m->omsg, p, &m->omsg.h.numAdditionals, &opt.resrec, opt.resrec.rroriginalttl, m->omsg.data + AbsoluteMaxDNSMessageData);
                        if (!p)
@@ -4165,8 +4895,6 @@ mDNSlocal void SendSPSRegistration(mDNS *const m, NetworkInterfaceInfo *intf, co
                        else
                                {
                                mStatus err;
-                               // Once we've attempted to register, we need to include our OWNER option in our packets when we re-awaken
-                               m->SentSleepProxyRegistration = mDNStrue;
 
                                LogSPS("SendSPSRegistration: Sending Update %s %d (%d) id %5d with %d records %d bytes to %#a:%d", intf->ifname, intf->NextSPSAttempt, sps,
                                        mDNSVal16(m->omsg.h.id), m->omsg.h.mDNS_numUpdates, p - m->omsg.data, &intf->SPSAddr[sps], mDNSVal16(intf->SPSPort[sps]));
@@ -4190,6 +4918,31 @@ exit:
        if (mDNSOpaque16IsZero(id) && intf->NextSPSAttempt < 8) intf->NextSPSAttempt++;
        }
 
+mDNSlocal mDNSBool RecordIsFirstOccurrenceOfOwner(mDNS *const m, const AuthRecord *const rr)
+       {
+       AuthRecord *ar;
+       for (ar = m->ResourceRecords; ar && ar != rr; ar=ar->next)
+               if (mDNSPlatformMemSame(&rr->WakeUp, &ar->WakeUp, sizeof(rr->WakeUp))) return mDNSfalse;
+       return mDNStrue;
+       }
+
+mDNSlocal void SendSPSRegistration(mDNS *const m, NetworkInterfaceInfo *const intf, const mDNSOpaque16 id)
+       {
+       AuthRecord *ar;
+       OwnerOptData owner = zeroOwner;
+
+       SendSPSRegistrationForOwner(m, intf, id, &owner);
+
+       for (ar = m->ResourceRecords; ar; ar=ar->next)
+               {
+               if (!mDNSPlatformMemSame(&owner, &ar->WakeUp, sizeof(owner)) && RecordIsFirstOccurrenceOfOwner(m, ar))
+                       {
+                       owner = ar->WakeUp;
+                       SendSPSRegistrationForOwner(m, intf, id, &owner);
+                       }
+               }
+       }
+
 // RetrySPSRegistrations is called from SendResponses, with the lock held
 mDNSlocal void RetrySPSRegistrations(mDNS *const m)
        {
@@ -4428,11 +5181,6 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep)
                        {
                        m->SleepState = SleepState_Awake;
                        m->SleepSeqNum++;
-                       if (m->SentSleepProxyRegistration)              // Include OWNER option in packets for 60 seconds after waking
-                               {
-                               m->SentSleepProxyRegistration = mDNSfalse;
-                               m->AnnounceOwner = NonZeroTime(m->timenow + 60 * mDNSPlatformOneSecond);
-                               }
                        // If the machine wakes and then immediately tries to sleep again (e.g. a maintenance wake)
                        // then we enforce a minimum delay of 16 seconds before we begin sleep processing.
                        // This is to allow time for the Ethernet link to come up, DHCP to get an address, mDNS to issue queries, etc.,
@@ -5982,7 +6730,7 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m,
                                                        // If we'd previously verified this record, put it back to probing state and try again
                                                        if (rr->resrec.RecordType == kDNSRecordTypeVerified)
                                                                {
-                                                               LogMsg("mDNSCoreReceiveResponse: Reseting to Probing: %s", ARDisplayString(m, rr));
+                                                               LogMsg("mDNSCoreReceiveResponse: Resetting to Probing: %s", ARDisplayString(m, rr));
                                                                rr->resrec.RecordType     = kDNSRecordTypeUnique;
                                                                // We set ProbeCount to one more than the usual value so we know we've already touched this record.
                                                                // This is because our single probe for "example-name.local" could yield a response with (say) two A records and
@@ -5996,7 +6744,7 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m,
                                                        // If we're probing for this record, we just failed
                                                        else if (rr->resrec.RecordType == kDNSRecordTypeUnique)
                                                                {
-                                                               LogMsg("mDNSCoreReceiveResponse: ProbeCount %d; will rename %s", rr->ProbeCount, ARDisplayString(m, rr));
+                                                               LogMsg("mDNSCoreReceiveResponse: ProbeCount %d; will deregister %s", rr->ProbeCount, ARDisplayString(m, rr));
                                                                mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict);
                                                                }
                                                        // We assumed this record must be unique, but we were wrong. (e.g. There are two mDNSResponders on the
@@ -6295,6 +7043,17 @@ exit:
                ptr = getQuestion(response, ptr, end, InterfaceID, &q);
                if (ptr && (qptr = ExpectingUnicastResponseForQuestion(m, dstport, response->h.id, &q, !dstaddr)))
                        {
+                       CacheRecord *rr, *neg = mDNSNULL;
+                       mDNSu32 slot = HashSlot(&q.qname);
+                       CacheGroup *cg = CacheGroupForName(m, slot, q.qnamehash, &q.qname);
+                       for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
+                               if (SameNameRecordAnswersQuestion(&rr->resrec, qptr))
+                                       {
+                                       // 1. If we got a fresh answer to this query, then don't need to generate a negative entry
+                                       if (RRExpireTime(rr) - m->timenow > 0) break;
+                                       // 2. If we already had a negative entry, keep track of it so we can resurrect it instead of creating a new one
+                                       if (rr->resrec.RecordType == kDNSRecordTypePacketNegative) neg = rr;
+                                       }
                        // When we're doing parallel unicast and multicast queries for dot-local names (for supporting Microsoft
                        // Active Directory sites) we don't want to waste memory making negative cache entries for all the unicast answers.
                        // Otherwise we just fill up our cache with negative entries for just about every single multicast name we ever look up
@@ -6306,21 +7065,20 @@ exit:
                        // in conflict with the mDNS spec, because that spec says, "Multicast DNS Zones have no SOA record," so it's okay to cache
                        // negative answers for "local. SOA" from a uDNS server, because the mDNS spec already says that such records do not exist :-)
                        if (!InterfaceID && q.qtype != kDNSType_SOA && IsLocalDomain(&q.qname))
-                               LogInfo("Skipping check to see if we need to generate a negative cache entry for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
+                               {
+                               // If we did not find a positive answer and we can append search domains to this question,
+                               // generate a negative response (without creating a cache entry) to append search domains.
+                               if (qptr->AppendSearchDomains && !rr)
+                                       {
+                                       LogInfo("mDNSCoreReceiveResponse: Generate negative response for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
+                                       m->CurrentQuestion = qptr;
+                                       GenerateNegativeResponse(m);
+                                       m->CurrentQuestion = mDNSNULL;
+                                       }
+                               else LogInfo("mDNSCoreReceiveResponse: Skipping check to see if we need to generate a negative cache entry for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
+                               }
                        else
                                {
-                               CacheRecord *rr, *neg = mDNSNULL;
-                               mDNSu32 slot = HashSlot(&q.qname);
-                               CacheGroup *cg = CacheGroupForName(m, slot, q.qnamehash, &q.qname);
-                               for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
-                                       if (SameNameRecordAnswersQuestion(&rr->resrec, qptr))
-                                               {
-                                               // 1. If we got a fresh answer to this query, then don't need to generate a negative entry
-                                               if (RRExpireTime(rr) - m->timenow > 0) break;
-                                               // 2. If we already had a negative entry, keep track of it so we can resurrect it instead of creating a new one
-                                               if (rr->resrec.RecordType == kDNSRecordTypePacketNegative) neg = rr;
-                                               }
-       
                                if (!rr)
                                        {
                                        // We start off assuming a negative caching TTL of 60 seconds
@@ -6410,16 +7168,29 @@ exit:
                }
        }
 
+// ScheduleWakeup causes all proxy records with WakeUp.HMAC matching mDNSEthAddr 'e' to be deregistered, causing
+// multiple wakeup magic packets to be sent if appropriate, and all records to be ultimately freed after a few seconds.
+// ScheduleWakeup is called on mDNS record conflicts, ARP conflicts, NDP conflicts, or reception of trigger traffic
+// that warrants waking the sleeping host.
+// ScheduleWakeup must be called with the lock held (ScheduleWakeupForList uses mDNS_Deregister_internal)
+
 mDNSlocal void ScheduleWakeupForList(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *e, AuthRecord *const thelist)
        {
+       // We don't need to use the m->CurrentRecord mechanism here because the target HMAC is nonzero,
+       // so all we're doing is marking the record to generate a few wakeup packets
        AuthRecord *rr;
-       for (rr = thelist; rr; rr=rr->next)
+       if (!e->l[0]) { LogMsg("ScheduleWakeupForList ERROR: Target HMAC is zero"); return; }
+       for (rr = thelist; rr; rr = rr->next) 
                if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering && mDNSSameEthAddress(&rr->WakeUp.HMAC, e))
+                       {
+                       LogInfo("ScheduleWakeupForList: Scheduling wakeup packets for %s", ARDisplayString(m, rr));
                        mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
+                       }
        }
 
 mDNSlocal void ScheduleWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *e)
        {
+       if (!e->l[0]) { LogMsg("ScheduleWakeup ERROR: Target HMAC is zero"); return; }
        ScheduleWakeupForList(m, InterfaceID, e, m->DuplicateRecords);
        ScheduleWakeupForList(m, InterfaceID, e, m->ResourceRecords);
        }
@@ -6431,11 +7202,17 @@ mDNSlocal void SPSRecordCallback(mDNS *const m, AuthRecord *const ar, mStatus re
 
        if (result == mStatus_NameConflict)
                {
-               LogMsg("Received Conflicting mDNS -- waking %s %.6a %s", InterfaceNameForID(m, ar->resrec.InterfaceID), &ar->WakeUp.HMAC, ARDisplayString(m, ar));
-               SendWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.IMAC, &ar->WakeUp.password);
-               ScheduleWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.HMAC);
+               mDNS_Lock(m);
+               LogMsg("%-7s Conflicting mDNS -- waking %.6a %s", InterfaceNameForID(m, ar->resrec.InterfaceID), &ar->WakeUp.HMAC, ARDisplayString(m, ar));
+               if (ar->WakeUp.HMAC.l[0])
+                       {
+                       SendWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.IMAC, &ar->WakeUp.password);  // Send one wakeup magic packet
+                       ScheduleWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.HMAC);                                    // Schedule all other records with the same owner to be woken
+                       }
+               mDNS_Unlock(m);
                }
-       else if (result == mStatus_MemFree)
+
+       if (result == mStatus_NameConflict || result == mStatus_MemFree)
                {
                m->ProxyRecords--;
                mDNSPlatformMemFree(ar);
@@ -6534,7 +7311,7 @@ mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m,
                                        m->rec.r.resrec.rrclass &= ~kDNSClass_UniqueRRSet;
                                        ClearIdenticalProxyRecords(m, &owner, m->DuplicateRecords);     // Make sure we don't have any old stale duplicates of this record
                                        ClearIdenticalProxyRecords(m, &owner, m->ResourceRecords);
-                                       mDNS_SetupResourceRecord(ar, mDNSNULL, InterfaceID, m->rec.r.resrec.rrtype, m->rec.r.resrec.rroriginalttl, RecordType, SPSRecordCallback, ar);
+                                       mDNS_SetupResourceRecord(ar, mDNSNULL, InterfaceID, m->rec.r.resrec.rrtype, m->rec.r.resrec.rroriginalttl, RecordType, AuthRecordAny, SPSRecordCallback, ar);
                                        AssignDomainName(&ar->namestorage, m->rec.r.resrec.name);
                                        ar->resrec.rdlength = GetRDLength(&m->rec.r.resrec, mDNSfalse);
                                        ar->resrec.rdata->MaxRDLength = RDLengthMem;
@@ -6577,7 +7354,7 @@ mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m,
                        }
                else
                        {
-                       mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
+                       mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
                        opt.resrec.rrclass    = NormalMaxDNSMessageData;
                        opt.resrec.rdlength   = sizeof(rdataOPT);       // One option in this OPT record
                        opt.resrec.rdestimate = sizeof(rdataOPT);
@@ -6594,7 +7371,6 @@ mDNSlocal void mDNSCoreReceiveUpdateR(mDNS *const m, const DNSMessage *const msg
        {
        if (InterfaceID)
                {
-               AuthRecord *rr;
                mDNSu32 updatelease = 60 * 60;          // If SPS fails to indicate lease time, assume one hour
                const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space);
                if (ptr)
@@ -6614,15 +7390,30 @@ mDNSlocal void mDNSCoreReceiveUpdateR(mDNS *const m, const DNSMessage *const msg
                        m->rec.r.resrec.RecordType = 0;         // Clear RecordType to show we're not still using it
                        }
 
-               for (rr = m->ResourceRecords; rr; rr=rr->next)
+               if (m->CurrentRecord)
+                       LogMsg("mDNSCoreReceiveUpdateR ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
+               m->CurrentRecord = m->ResourceRecords;
+               while (m->CurrentRecord)
+                       {
+                       AuthRecord *const rr = m->CurrentRecord;
                        if (rr->resrec.InterfaceID == InterfaceID || (!rr->resrec.InterfaceID && (rr->ForceMCast || IsLocalDomain(rr->resrec.name))))
                                if (mDNSSameOpaque16(rr->updateid, msg->h.id))
                                        {
                                        rr->updateid = zeroID;
                                        rr->expire   = NonZeroTime(m->timenow + updatelease * mDNSPlatformOneSecond);
-                                       LogSPS("Sleep Proxy registered record %5d %s", updatelease, ARDisplayString(m,rr));
+                                       LogSPS("Sleep Proxy %s record %5d %s", rr->WakeUp.HMAC.l[0] ? "transferred" : "registered", updatelease, ARDisplayString(m,rr));
+                                       if (rr->WakeUp.HMAC.l[0])
+                                               {
+                                               rr->WakeUp.HMAC = zeroEthAddr;  // Clear HMAC so that mDNS_Deregister_internal doesn't waste packets trying to wake this host
+                                               rr->RequireGoodbye = mDNSfalse; // and we don't want to send goodbye for it
+                                               mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
+                                               }
                                        }
-
+                       // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because
+                       // new records could have been added to the end of the list as a result of that call.
+                       if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now
+                               m->CurrentRecord = rr->next;
+                       }
                }
        // If we were waiting to go to sleep, then this SPS registration or wide-area record deletion
        // may have been the thing we were waiting for, so schedule another check to see if we can sleep now.
@@ -6829,10 +7620,25 @@ mDNSlocal DNSQuestion *FindDuplicateQuestion(const mDNS *const m, const DNSQuest
 mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, DNSQuestion *const question)
        {
        DNSQuestion *q;
+       DNSQuestion *first = mDNSNULL;
+
+       // This is referring to some other question as duplicate. No other question can refer to this
+       // question as a duplicate.
+       if (question->DuplicateOf)
+               {
+               LogInfo("UpdateQuestionDuplicates: question %p %##s (%s) duplicate of %p %##s (%s)",
+                       question, question->qname.c, DNSTypeName(question->qtype),
+                       question->DuplicateOf, question->DuplicateOf->qname.c, DNSTypeName(question->DuplicateOf->qtype));
+               return;
+               }
+
        for (q = m->Questions; q; q=q->next)            // Scan our list of questions
                if (q->DuplicateOf == question)                 // To see if any questions were referencing this as their duplicate
-                       if ((q->DuplicateOf = FindDuplicateQuestion(m, q)) == mDNSNULL)
+                       {
+                       q->DuplicateOf = first;
+                       if (!first)
                                {
+                               first = q;
                                // If q used to be a duplicate, but now is not,
                                // then inherit the state from the question that's going away
                                q->LastQTime         = question->LastQTime;
@@ -6886,6 +7692,51 @@ mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, DNSQuestion *const questi
 
                                SetNextQueryTime(m,q);
                                }
+                       }
+       }
+
+mDNSexport McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, mDNSu32 timeout)
+       {
+       McastResolver **p = &m->McastResolvers;
+       McastResolver *tmp = mDNSNULL;
+       
+       if (!d) d = (const domainname *)"";
+
+       LogInfo("mDNS_AddMcastResolver: Adding %##s, InterfaceID %p, timeout %u", d->c, interface, timeout);
+
+       if (m->mDNS_busy != m->mDNS_reentrancy+1)
+               LogMsg("mDNS_AddMcastResolver: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+
+       while (*p)      // Check if we already have this {interface, domain} tuple registered
+               {
+               if ((*p)->interface == interface && SameDomainName(&(*p)->domain, d))
+                       {
+                       if (!((*p)->flags & DNSServer_FlagDelete)) LogMsg("Note: Mcast Resolver domain %##s (%p) registered more than once", d->c, interface);
+                       (*p)->flags &= ~DNSServer_FlagDelete;
+                       tmp = *p;
+                       *p = tmp->next;
+                       tmp->next = mDNSNULL;
+                       }
+               else
+                       p=&(*p)->next;
+               }
+
+       if (tmp) *p = tmp; // move to end of list, to ensure ordering from platform layer
+       else
+               {
+               // allocate, add to list
+               *p = mDNSPlatformMemAllocate(sizeof(**p));
+               if (!*p) LogMsg("mDNS_AddMcastResolver: ERROR!! - malloc");
+               else
+                       {
+                       (*p)->interface = interface;
+                       (*p)->flags     = DNSServer_FlagNew;
+                       (*p)->timeout   = timeout;
+                       AssignDomainName(&(*p)->domain, d);
+                       (*p)->next = mDNSNULL;
+                       }
+               }
+       return(*p);
        }
 
 mDNSinline mDNSs32 PenaltyTimeForServer(mDNS *m, DNSServer *server)
@@ -6939,14 +7790,42 @@ mDNSlocal int BetterMatchForName(const domainname *name, int namecount, const do
                return -1;
        }
 
+// Normally, we have McastResolvers for .local, in-addr.arpa and ip6.arpa. But there
+// can be queries that can forced to multicast (ForceMCast) even though they don't end in these
+// names. In that case, we give a default timeout of 5 seconds
+#define DEFAULT_MCAST_TIMEOUT  5
+mDNSlocal mDNSu32 GetTimeoutForMcastQuestion(mDNS *m, DNSQuestion *question)
+       {
+       McastResolver *curmatch = mDNSNULL;
+       int bestmatchlen = -1, namecount = CountLabels(&question->qname);
+       McastResolver *curr;
+       int bettermatch, currcount;
+       for (curr = m->McastResolvers; curr; curr = curr->next)
+               {
+               currcount = CountLabels(&curr->domain);
+               bettermatch = BetterMatchForName(&question->qname, namecount, &curr->domain, currcount, bestmatchlen);
+               // Take the first best match. If there are multiple equally good matches (bettermatch = 0), we take
+               // the timeout value from the first one
+               if (bettermatch == 1)
+                       {
+                       curmatch = curr;
+                       bestmatchlen = currcount;
+                       }
+               }
+       LogInfo("GetTimeoutForMcastQuestion: question %##s curmatch %p, Timeout %d", question->qname.c, curmatch,
+               curmatch ? curmatch->timeout : DEFAULT_MCAST_TIMEOUT);
+       return ( curmatch ? curmatch->timeout : DEFAULT_MCAST_TIMEOUT);
+       }
+
 // Sets all the Valid DNS servers for a question
-mDNSexport void SetValidDNSServers(mDNS *m, DNSQuestion *question)
+mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question)
        {
        DNSServer *curmatch = mDNSNULL;
        int bestmatchlen = -1, namecount = CountLabels(&question->qname);
        DNSServer *curr;
        int bettermatch, currcount;
        int index = 0;
+       mDNSu32 timeout = 0;
 
        question->validDNSServers = zeroOpaque64;
        for (curr = m->DNSServers; curr; curr = curr->next)
@@ -6956,6 +7835,17 @@ mDNSexport void SetValidDNSServers(mDNS *m, DNSQuestion *question)
                if (curr->flags & DNSServer_FlagDelete)
                        { debugf("SetValidDNSServers: Delete set for index %d, DNS server %#a (Domain %##s), scoped %d", index, &curr->addr, curr->domain.c, curr->scoped); continue; }
 
+               // This happens normally when you unplug the interface where we reset the interfaceID to mDNSInterface_Any for all
+               // the DNS servers whose scope match the interfaceID. Few seconds later, we also receive the updated DNS configuration.
+               // But any questions that has mDNSInterface_Any scope that are started/restarted before we receive the update
+               // (e.g., CheckSuppressUnusableQuestions is called when interfaces are deregistered with the core) should not
+               // match the scoped entries by mistake. 
+               //
+               // Note: DNS configuration change will help pick the new dns servers but currently it does not affect the timeout
+
+               if (curr->scoped && curr->interface == mDNSInterface_Any)
+                       { debugf("SetValidDNSServers: Scoped DNS server %#a (Domain %##s) with Interface Any", &curr->addr, curr->domain.c); continue; }
+
                currcount = CountLabels(&curr->domain);
                if ((!curr->scoped && (!question->InterfaceID || (question->InterfaceID == mDNSInterface_Unicast))) || (curr->interface == question->InterfaceID))
                        {
@@ -6969,16 +7859,22 @@ mDNSexport void SetValidDNSServers(mDNS *m, DNSQuestion *question)
                                {
                                curmatch = curr;
                                bestmatchlen = currcount;
-                               if (bettermatch) { debugf("SetValidDNSServers: Resetting all the bits"); question->validDNSServers = zeroOpaque64; }
-                               debugf("SetValidDNSServers: Setting the bit for DNS server Address %#a (Domain %##s), Scoped:%d index %d", &curr->addr, curr->domain.c, curr->scoped, index);
+                               if (bettermatch) { debugf("SetValidDNSServers: Resetting all the bits"); question->validDNSServers = zeroOpaque64; timeout = 0; }
+                               debugf("SetValidDNSServers: question %##s Setting the bit for DNS server Address %#a (Domain %##s), Scoped:%d index %d,"
+                                       " Timeout %d, interface %p", question->qname.c, &curr->addr, curr->domain.c, curr->scoped, index, curr->timeout,
+                                       curr->interface);
+                               timeout += curr->timeout;
                                bit_set_opaque64(question->validDNSServers, index);
                                }
                        }
                index++;
                }
        question->noServerResponse = 0;
+       
        debugf("SetValidDNSServers: ValidDNSServer bits  0x%x%x for question %p %##s (%s)",
                question->validDNSServers.l[1], question->validDNSServers.l[0], question, question->qname.c, DNSTypeName(question->qtype));
+       // If there are no matching resolvers, then use the default value to timeout
+       return (timeout ? timeout : DEFAULT_UDNS_TIMEOUT);
        }
 
 // Get the Best server that matches a name. If you find penalized servers, look for the one
@@ -7186,15 +8082,12 @@ mDNSlocal mDNSBool ShouldSuppressQuery(mDNS *const m, domainname *qname, mDNSu16
        return mDNStrue;
        }
 
-mDNSlocal void CheckSuppressedCurrentQuestion(mDNS *const m, DNSQuestion *q)
+mDNSlocal void CacheRecordRmvEventsForCurrentQuestion(mDNS *const m, DNSQuestion *q)
        {
        CacheRecord *rr;
        mDNSu32 slot;
        CacheGroup *cg;
 
-       // Temporarily turn off suppression so that AnswerCurrentQuestionWithResourceRecord
-       // can answer the question
-       q->SuppressQuery = mDNSfalse;
        slot = HashSlot(&q->qname);
        cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
        for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
@@ -7202,15 +8095,15 @@ mDNSlocal void CheckSuppressedCurrentQuestion(mDNS *const m, DNSQuestion *q)
                // Don't deliver RMV events for negative records
                if (rr->resrec.RecordType == kDNSRecordTypePacketNegative)
                        {
-                       LogInfo("CheckSuppressedCurrentQuestion: CacheRecord %s Suppressing RMV events for question %p %##s (%s), CRActiveQuestion %p, CurrentAnswers %d",
+                       LogInfo("CacheRecordRmvEventsForCurrentQuestion: CacheRecord %s Suppressing RMV events for question %p %##s (%s), CRActiveQuestion %p, CurrentAnswers %d",
                                CRDisplayString(m, rr), q, q->qname.c, DNSTypeName(q->qtype), rr->CRActiveQuestion, q->CurrentAnswers);
                        continue;
                        }
 
                if (SameNameRecordAnswersQuestion(&rr->resrec, q))
                        {
-                       LogInfo("CheckSuppressedCurrentQuestion: Calling AnswerCurrentQuestionWithResourceRecord (RMV) for question %##s using resource record %s",
-                               q->qname.c, CRDisplayString(m, rr));
+                       LogInfo("CacheRecordRmvEventsForCurrentQuestion: Calling AnswerCurrentQuestionWithResourceRecord (RMV) for question %##s using resource record %s LocalAnswers %d",
+                               q->qname.c, CRDisplayString(m, rr), q->LOAddressAnswers);
 
                        q->CurrentAnswers--;
                        if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--;
@@ -7225,11 +8118,11 @@ mDNSlocal void CheckSuppressedCurrentQuestion(mDNS *const m, DNSQuestion *q)
                                // when the cache entry is about to expire, we won't find an active question
                                // (pointed by CRActiveQuestion) to refresh the cache.
                                for (qptr = m->Questions; qptr; qptr=qptr->next)
-                                       if (ActiveQuestion(qptr) && ResourceRecordAnswersQuestion(&rr->resrec, qptr))
+                                       if (qptr != q && ActiveQuestion(qptr) && ResourceRecordAnswersQuestion(&rr->resrec, qptr))
                                                break;
 
                                if (qptr)
-                                       LogInfo("CheckSuppressedCurrentQuestion: Updating CRActiveQuestion to %p for cache record %s, "
+                                       LogInfo("CacheRecordRmvEventsForCurrentQuestion: Updating CRActiveQuestion to %p for cache record %s, "
                                                "Original question CurrentAnswers %d, new question CurrentAnswers %d, SuppressUnusable %d, SuppressQuery %d",
                                                qptr, CRDisplayString(m,rr), q->CurrentAnswers, qptr->CurrentAnswers, qptr->SuppressUnusable, qptr->SuppressQuery);
 
@@ -7240,7 +8133,6 @@ mDNSlocal void CheckSuppressedCurrentQuestion(mDNS *const m, DNSQuestion *q)
                        if (m->CurrentQuestion != q) break;             // If callback deleted q, then we're finished here
                        }
                }
-               if (m->CurrentQuestion == q) q->SuppressQuery = mDNStrue;
        }
 
 mDNSlocal mDNSBool IsQuestionNew(mDNS *const m, DNSQuestion *question)
@@ -7251,10 +8143,72 @@ mDNSlocal mDNSBool IsQuestionNew(mDNS *const m, DNSQuestion *question)
        return mDNSfalse;
        }
 
+mDNSlocal mDNSBool LocalRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q)
+       {
+       AuthRecord *rr;
+       mDNSu32 slot;
+       AuthGroup *ag;
+
+       if (m->CurrentQuestion)
+               LogMsg("LocalRecordRmvEventsForQuestion: ERROR m->CurrentQuestion already set: %##s (%s)",
+                       m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
+
+       if (IsQuestionNew(m, q))
+               {
+               LogInfo("LocalRecordRmvEventsForQuestion: New Question %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+               return mDNStrue;
+               }
+       m->CurrentQuestion = q;
+       slot = AuthHashSlot(&q->qname);
+       ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname);
+       if (ag)
+               {
+               for (rr = ag->members; rr; rr=rr->next)
+                       // Filter the /etc/hosts records - LocalOnly, Unique, A/AAAA/CNAME
+                       if (LORecordAnswersAddressType(rr) && LocalOnlyRecordAnswersQuestion(rr, q))
+                               {
+                               LogInfo("LocalRecordRmvEventsForQuestion: Delivering possible Rmv events with record %s",
+                                       ARDisplayString(m, rr));
+                               if (q->CurrentAnswers <= 0 || q->LOAddressAnswers <= 0)
+                                       {
+                                       LogMsg("LocalRecordRmvEventsForQuestion: ERROR!! CurrentAnswers or LOAddressAnswers is zero %p %##s"
+                                               " (%s) CurrentAnswers %d, LOAddressAnswers %d", q, q->qname.c, DNSTypeName(q->qtype),
+                                               q->CurrentAnswers, q->LOAddressAnswers);
+                                       continue;
+                                       }
+                               AnswerLocalQuestionWithLocalAuthRecord(m, rr, QC_rmv);          // MUST NOT dereference q again
+                               if (m->CurrentQuestion != q) { m->CurrentQuestion = mDNSNULL; return mDNSfalse; }
+                               }
+               }
+       m->CurrentQuestion = mDNSNULL;
+       return mDNStrue;
+       }
+
+// Returns false if the question got deleted while delivering the RMV events
+// The caller should handle the case 
+mDNSlocal mDNSBool CacheRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q)
+       {
+       if (m->CurrentQuestion)
+               LogMsg("CacheRecordRmvEventsForQuestion: ERROR m->CurrentQuestion already set: %##s (%s)",
+                       m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
+
+       // If it is a new question, we have not delivered any ADD events yet. So, don't deliver RMV events.
+       // If this question was answered using local auth records, then you can't deliver RMVs using cache
+       if (!IsQuestionNew(m, q) && !q->LOAddressAnswers) 
+               {
+               m->CurrentQuestion = q;
+               CacheRecordRmvEventsForCurrentQuestion(m, q);
+               if (m->CurrentQuestion != q) { m->CurrentQuestion = mDNSNULL; return mDNSfalse; }
+               m->CurrentQuestion = mDNSNULL;
+               }
+       else { LogInfo("CacheRecordRmvEventsForQuestion: Question %p %##s (%s) is a new question", q, q->qname.c, DNSTypeName(q->qtype)); }
+       return mDNStrue;
+       }
+
 // The caller should hold the lock
 mDNSexport void CheckSuppressUnusableQuestions(mDNS *const m)
        {
-       DNSQuestion *q, *qnext;
+       DNSQuestion *q;
        DNSQuestion *restart = mDNSNULL;
 
        // We look through all questions including new questions. During network change events,
@@ -7262,38 +8216,45 @@ mDNSexport void CheckSuppressUnusableQuestions(mDNS *const m)
        // which may be suppressed at this instance. Before it is handled we get another network
        // event that changes the status e.g., address becomes available. If we did not process
        // new questions, we would never change its SuppressQuery status.
-       for (q = m->Questions; q ; q = qnext)
-               {
-               qnext = q->next;
+       //
+       // CurrentQuestion is used by RmvEventsForQuestion below. While delivering RMV events, the
+       // application callback can potentially stop the current question (detected by CurrentQuestion) or
+       // *any* other question which could be the next one that we may process here. RestartQuestion
+       // points to the "next" question which will be automatically advanced in mDNS_StopQuery_internal
+       // if the "next" question is stopped while the CurrentQuestion is stopped
+       if (m->RestartQuestion)
+               LogMsg("CheckSuppressUnusableQuestions: ERROR!! m->RestartQuestion already set: %##s (%s)",
+                       m->RestartQuestion->qname.c, DNSTypeName(m->RestartQuestion->qtype));
+       m->RestartQuestion = m->Questions;
+       while (m->RestartQuestion)
+               {
+               q = m->RestartQuestion;
+               m->RestartQuestion = q->next;
                if (!mDNSOpaque16IsZero(q->TargetQID) && q->SuppressUnusable)
                        {
                        mDNSBool old = q->SuppressQuery;
                        q->SuppressQuery = ShouldSuppressQuery(m, &q->qname, q->qtype, q->InterfaceID);
                        if (q->SuppressQuery != old)
                                {
-                               if (q->SuppressQuery)
-                                       {
-                                       // Previously it was not suppressed, Generate RMV events for the ADDs that we might have delivered before
-                                       // followed by a negative cache response
-                                       if (m->CurrentQuestion)
-                                               LogMsg("CheckSuppressUnusableQuestions: ERROR m->CurrentQuestion already set: %##s (%s)",
-                                                       m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
-
-                                       // If it is a new question, we have not delivered any ADD events yet. So, don't deliver RMV events.
-                                       if (!IsQuestionNew(m, q))
-                                               {
-                                               m->CurrentQuestion = q;
-                                               CheckSuppressedCurrentQuestion(m, q);
-                                               if (m->CurrentQuestion != q)
-                                                       {
-                                                       m->CurrentQuestion = mDNSNULL;
-                                                       LogInfo("CheckSuppressUnusableQuestions: Question deleted while giving RMV events");
-                                                       continue;
-                                                       }
-                                               m->CurrentQuestion = mDNSNULL;
-                                               }
-                                       else { debugf("CheckSuppressUnusableQuestion: Question %p %##s (%s) is a new question", q, q->qname.c, DNSTypeName(q->qtype)); }
-                                       }
+                               // NOTE: CacheRecordRmvEventsForQuestion will not generate RMV events for queries that have non-zero
+                               // LOddressAnswers. Hence it is important that we call CacheRecordRmvEventsForQuestion before
+                               // LocalRecordRmvEventsForQuestion (which decrements LOAddressAnswers)
+
+                               if (q->SuppressQuery)
+                                       {
+                                       // Previously it was not suppressed, Generate RMV events for the ADDs that we might have delivered before
+                                       // followed by a negative cache response. Temporarily turn off suppression so that
+                                       // AnswerCurrentQuestionWithResourceRecord can answer the question
+                                       q->SuppressQuery = mDNSfalse;
+                                       if (!CacheRecordRmvEventsForQuestion(m, q)) { LogInfo("CheckSuppressUnusableQuestions: Question deleted while delivering RMV events"); continue; }
+                                       q->SuppressQuery = mDNStrue;
+                                       }
+
+                               // SuppressUnusable does not affect questions that are answered from the local records (/etc/hosts)
+                               // and SuppressQuery status does not mean anything for these questions. As we are going to stop the
+                               // question below, we need to deliver the RMV events so that the ADDs that will be delivered during
+                               // the restart will not be a duplicate ADD
+                               if (!LocalRecordRmvEventsForQuestion(m, q)) { LogInfo("CheckSuppressUnusableQuestions: Question deleted while delivering RMV events"); continue; }
 
                                // There are two cases here.
                                //
@@ -7308,8 +8269,8 @@ mDNSexport void CheckSuppressUnusableQuestions(mDNS *const m)
                                // is a duplicate of non-SuppressUnusable question if it is not suppressed (SuppressQuery is false).
                                // A SuppressUnusable question is not a duplicate of non-SuppressUnusable question if it is suppressed
                                // (SuppressQuery is true). The reason for this is that when a question is suppressed, we want an
-                               // immediate response and not want to be blocked behind a question that is querying DNS servers.
-                               // When the question is not suppressed, we don't want two active questions sending packets on the wire.
+                               // immediate response and not want to be blocked behind a question that is querying DNS servers. When 
+                               // the question is not suppressed, we don't want two active questions sending packets on the wire.
                                // This affects both efficiency and also the current design where there is only one active question
                                // pointed to from a cache entry.
                                //
@@ -7343,8 +8304,8 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu
        {
        if (question->Target.type && !ValidQuestionTarget(question))
                {
-               LogMsg("Warning! Target.type = %ld port = %u (Client forgot to initialize before calling mDNS_StartQuery?)",
-                       question->Target.type, mDNSVal16(question->TargetPort));
+               LogMsg("mDNS_StartQuery_internal: Warning! Target.type = %ld port = %u (Client forgot to initialize before calling mDNS_StartQuery? for question %##s)",
+                       question->Target.type, mDNSVal16(question->TargetPort), question->qname.c);
                question->Target.type = mDNSAddrType_None;
                }
 
@@ -7410,6 +8371,7 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu
                question->CurrentAnswers    = 0;
                question->LargeAnswers      = 0;
                question->UniqueAnswers     = 0;
+               question->LOAddressAnswers  = 0;
                question->FlappingInterface1 = mDNSNULL;
                question->FlappingInterface2 = mDNSNULL;
                // Must do AuthInfo and SuppressQuery before calling FindDuplicateQuestion()
@@ -7447,7 +8409,8 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu
                question->id                = zeroOpaque64;
                question->validDNSServers   = zeroOpaque64;
                question->triedAllServersOnce = 0;
-               question->noServerResponse = 0;
+               question->noServerResponse  = 0;
+               question->StopTime = 0;
                if (question->WakeOnResolve)
                        {
                        question->WakeOnResolveCount = InitialWakeOnResolveCount;
@@ -7490,21 +8453,28 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu
                                // Duplicate questions should have the same DNSServers so that when we find
                                // a matching resource record, all of them get the answers. Calling GetServerForQuestion
                                // for the duplicate question may get a different DNS server from the original question
+                               mDNSu32 timeout = SetValidDNSServers(m, question);
+                               // We set the timeout whenever mDNS_StartQuery_internal is called. This means if we have
+                               // a networking change/search domain change that calls this function again we keep
+                               // reinitializing the timeout value which means it may never timeout. If this becomes
+                               // a common case in the future, we can easily fix this by adding extra state that
+                               // indicates that we have already set the StopTime.
+                               if (question->TimeoutQuestion)
+                                       question->StopTime = NonZeroTime(m->timenow + timeout * mDNSPlatformOneSecond);
                                if (question->DuplicateOf)
                                        {
                                        question->validDNSServers = question->DuplicateOf->validDNSServers;
                                        question->qDNSServer = question->DuplicateOf->qDNSServer;
-                                       LogInfo("mDNS_StartQuery_internal: Duplicate question %p (%p) %##s (%s), DNS Server %#a:%d",
-                                               question, question->DuplicateOf, question->qname.c, DNSTypeName(question->qtype),
+                                       LogInfo("mDNS_StartQuery_internal: Duplicate question %p (%p) %##s (%s), Timeout %d, DNS Server %#a:%d",
+                                               question, question->DuplicateOf, question->qname.c, DNSTypeName(question->qtype), timeout,
                                                question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL,
                                            mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort));
                                        }
                                else
                                        {
-                                       SetValidDNSServers(m, question);
                                        question->qDNSServer = GetServerForQuestion(m, question);
-                                       LogInfo("mDNS_StartQuery_internal: question %p %##s (%s), DNS Server %#a:%d",
-                                               question, question->qname.c, DNSTypeName(question->qtype),
+                                       LogInfo("mDNS_StartQuery_internal: question %p %##s (%s) Timeout %d, DNS Server %#a:%d",
+                                               question, question->qname.c, DNSTypeName(question->qtype), timeout,
                                                question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL,
                                            mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort));
                                        }
@@ -7527,6 +8497,12 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu
 #endif
                                        
                                }
+                       else
+                               {
+                               if (question->TimeoutQuestion)
+                                       question->StopTime = NonZeroTime(m->timenow + GetTimeoutForMcastQuestion(m, question) * mDNSPlatformOneSecond);
+                               }
+                       if (question->StopTime) SetNextQueryStopTime(m, question);
                        SetNextQueryTime(m,question);
                        }
 
@@ -7618,6 +8594,13 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que
 
        if (m->NewLocalOnlyQuestions == question) m->NewLocalOnlyQuestions = question->next;
 
+       if (m->RestartQuestion == question)
+               {
+               LogMsg("mDNS_StopQuery_internal: Just deleted the current restart question: %##s (%s)",
+                       question->qname.c, DNSTypeName(question->qtype));
+               m->RestartQuestion = question->next;
+               }
+
        // Take care not to trash question->next until *after* we've updated m->CurrentQuestion and m->NewQuestions
        question->next = mDNSNULL;
 
@@ -7759,7 +8742,12 @@ mDNSlocal mStatus mDNS_StartBrowse_internal(mDNS *const m, DNSQuestion *const qu
        question->ForceMCast       = ForceMCast;
        question->ReturnIntermed   = mDNSfalse;
        question->SuppressUnusable = mDNSfalse;
-       question->WakeOnResolve    = mDNSfalse;
+       question->SearchListIndex  = 0;
+       question->AppendSearchDomains = 0;
+       question->RetryWithSearchDomains = mDNSfalse;
+       question->TimeoutQuestion  = 0;
+       question->WakeOnResolve    = 0;
+       question->qnameOrig        = mDNSNULL;
        question->QuestionCallback = Callback;
        question->QuestionContext  = Context;
        if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) return(mStatus_BadParamErr);
@@ -7933,7 +8921,12 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m,
        query->qSRV.ForceMCast          = mDNSfalse;
        query->qSRV.ReturnIntermed      = mDNSfalse;
        query->qSRV.SuppressUnusable    = mDNSfalse;
-       query->qSRV.WakeOnResolve       = mDNSfalse;
+       query->qSRV.SearchListIndex     = 0;
+       query->qSRV.AppendSearchDomains = 0;
+       query->qSRV.RetryWithSearchDomains = mDNSfalse;
+       query->qSRV.TimeoutQuestion     = 0;
+       query->qSRV.WakeOnResolve       = 0;
+       query->qSRV.qnameOrig           = mDNSNULL;
        query->qSRV.QuestionCallback    = FoundServiceInfoSRV;
        query->qSRV.QuestionContext     = query;
 
@@ -7948,7 +8941,12 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m,
        query->qTXT.ForceMCast          = mDNSfalse;
        query->qTXT.ReturnIntermed      = mDNSfalse;
        query->qTXT.SuppressUnusable    = mDNSfalse;
-       query->qTXT.WakeOnResolve       = mDNSfalse;
+       query->qTXT.SearchListIndex     = 0;
+       query->qTXT.AppendSearchDomains = 0;
+       query->qTXT.RetryWithSearchDomains = mDNSfalse;
+       query->qTXT.TimeoutQuestion     = 0;
+       query->qTXT.WakeOnResolve       = 0;
+       query->qTXT.qnameOrig           = mDNSNULL;
        query->qTXT.QuestionCallback    = FoundServiceInfoTXT;
        query->qTXT.QuestionContext     = query;
 
@@ -7963,7 +8961,12 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m,
        query->qAv4.ForceMCast          = mDNSfalse;
        query->qAv4.ReturnIntermed      = mDNSfalse;
        query->qAv4.SuppressUnusable    = mDNSfalse;
-       query->qAv4.WakeOnResolve       = mDNSfalse;
+       query->qAv4.SearchListIndex     = 0;
+       query->qAv4.AppendSearchDomains = 0;
+       query->qAv4.RetryWithSearchDomains = mDNSfalse;
+       query->qAv4.TimeoutQuestion     = 0;
+       query->qAv4.WakeOnResolve       = 0;
+       query->qAv4.qnameOrig           = mDNSNULL;
        query->qAv4.QuestionCallback    = FoundServiceInfo;
        query->qAv4.QuestionContext     = query;
 
@@ -7978,7 +8981,12 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m,
        query->qAv6.ForceMCast          = mDNSfalse;
        query->qAv6.ReturnIntermed      = mDNSfalse;
        query->qAv6.SuppressUnusable    = mDNSfalse;
-       query->qAv6.WakeOnResolve       = mDNSfalse;
+       query->qAv6.SearchListIndex     = 0;
+       query->qAv6.AppendSearchDomains = 0;
+       query->qAv6.RetryWithSearchDomains = mDNSfalse;
+       query->qAv6.TimeoutQuestion     = 0;
+       query->qAv6.WakeOnResolve       = 0;
+       query->qAv6.qnameOrig           = mDNSNULL;
        query->qAv6.QuestionCallback    = FoundServiceInfo;
        query->qAv6.QuestionContext     = query;
 
@@ -8029,7 +9037,12 @@ mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, m
        question->ForceMCast       = mDNSfalse;
        question->ReturnIntermed   = mDNSfalse;
        question->SuppressUnusable = mDNSfalse;
-       question->WakeOnResolve    = mDNSfalse;
+       question->SearchListIndex  = 0;
+       question->AppendSearchDomains = 0;
+       question->RetryWithSearchDomains = mDNSfalse;
+       question->TimeoutQuestion  = 0;
+       question->WakeOnResolve    = 0;
+       question->qnameOrig        = mDNSNULL;
        question->QuestionCallback = Callback;
        question->QuestionContext  = Context;
        if (DomainType > mDNS_DomainTypeMax) return(mStatus_BadParamErr);
@@ -8082,7 +9095,7 @@ mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newt
        rr->UpdateCallback       = Callback;
 
 #ifndef UNICAST_DISABLED
-       if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly && rr->resrec.InterfaceID != mDNSInterface_P2P && !IsLocalDomain(rr->resrec.name))
+       if (rr->ARType != AuthRecordLocalOnly && rr->ARType != AuthRecordP2P && !IsLocalDomain(rr->resrec.name))
                {
                mStatus status = uDNS_UpdateRecord(m, rr);
                // The caller frees the memory on error, don't retain stale pointers
@@ -8092,8 +9105,8 @@ mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newt
                }
 #endif
 
-       if (rr->resrec.rroriginalttl == newttl &&
-               rr->resrec.rdlength == newrdlength && mDNSPlatformMemSame(rr->resrec.rdata->u.data, newrdata->u.data, newrdlength))
+       if (RRLocalOnly(rr) || (rr->resrec.rroriginalttl == newttl &&
+               rr->resrec.rdlength == newrdlength && mDNSPlatformMemSame(rr->resrec.rdata->u.data, newrdata->u.data, newrdlength)))
                CompleteRDataUpdate(m, rr);
        else
                {
@@ -8149,9 +9162,9 @@ mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set)
        if (!primary) primary = set; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary
 
        // Send dynamic update for non-linklocal IPv4 Addresses
-       mDNS_SetupResourceRecord(&set->RR_A,     mDNSNULL, set->InterfaceID, kDNSType_A,     kHostNameTTL, kDNSRecordTypeUnique,      mDNS_HostNameCallback, set);
-       mDNS_SetupResourceRecord(&set->RR_PTR,   mDNSNULL, set->InterfaceID, kDNSType_PTR,   kHostNameTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
-       mDNS_SetupResourceRecord(&set->RR_HINFO, mDNSNULL, set->InterfaceID, kDNSType_HINFO, kHostNameTTL, kDNSRecordTypeUnique,      mDNSNULL, mDNSNULL);
+       mDNS_SetupResourceRecord(&set->RR_A,     mDNSNULL, set->InterfaceID, kDNSType_A,     kHostNameTTL, kDNSRecordTypeUnique,      AuthRecordAny, mDNS_HostNameCallback, set);
+       mDNS_SetupResourceRecord(&set->RR_PTR,   mDNSNULL, set->InterfaceID, kDNSType_PTR,   kHostNameTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
+       mDNS_SetupResourceRecord(&set->RR_HINFO, mDNSNULL, set->InterfaceID, kDNSType_HINFO, kHostNameTTL, kDNSRecordTypeUnique,      AuthRecordAny, mDNSNULL, mDNSNULL);
 
 #if ANSWER_REMOTE_HOSTNAME_QUERIES
        set->RR_A    .AllowRemoteQuery  = mDNStrue;
@@ -8403,10 +9416,10 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s
        NetworkInterfaceInfo **p = &m->HostInterfaces;
 
        if (!set->InterfaceID)
-               { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with zero InterfaceID", &set->ip); return(mStatus_Invalid); }
+               { LogMsg("mDNS_RegisterInterface: Error! Tried to register a NetworkInterfaceInfo %#a with zero InterfaceID", &set->ip); return(mStatus_Invalid); }
 
        if (!mDNSAddressIsValidNonZero(&set->mask))
-               { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with invalid mask %#a", &set->ip, &set->mask); return(mStatus_Invalid); }
+               { LogMsg("mDNS_RegisterInterface: Error! Tried to register a NetworkInterfaceInfo %#a with invalid mask %#a", &set->ip, &set->mask); return(mStatus_Invalid); }
 
        mDNS_Lock(m);
        
@@ -8422,7 +9435,7 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s
                {
                if (*p == set)
                        {
-                       LogMsg("Error! Tried to register a NetworkInterfaceInfo that's already in the list");
+                       LogMsg("mDNS_RegisterInterface: Error! Tried to register a NetworkInterfaceInfo that's already in the list");
                        mDNS_Unlock(m);
                        return(mStatus_AlreadyRegistered);
                        }
@@ -8456,7 +9469,7 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s
        // giving the false impression that there's an active representative of this interface when there really isn't.
        // Therefore, when registering an interface, we want to re-trigger our questions and re-probe our Resource Records,
        // even if we believe that we previously had an active representative of this interface.
-       if (set->McastTxRx && ((m->KnownBugs & mDNS_KnownBug_PhantomInterfaces) || FirstOfType || set->InterfaceActive))
+       if (set->McastTxRx && (FirstOfType || set->InterfaceActive))
                {
                DNSQuestion *q;
                // Normally, after an interface comes up, we pause half a second before beginning probing.
@@ -8481,13 +9494,20 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s
                // See <rdar://problem/4073853> mDNS: m->SuppressSending set too enthusiastically
                if (!m->SuppressSending) m->SuppressSending = m->timenow + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval);
 
-               if (flapping) LogMsg("RegisterInterface: Frequent transitions for interface %s (%#a)", set->ifname, &set->ip);
+               if (flapping) LogMsg("mDNS_RegisterInterface: Frequent transitions for interface %s (%#a)", set->ifname, &set->ip);
 
-               LogInfo("RegisterInterface: %s (%#a) probedelay %d", set->ifname, &set->ip, probedelay);
+               LogInfo("mDNS_RegisterInterface: %s (%#a) probedelay %d", set->ifname, &set->ip, probedelay);
                if (m->SuppressProbes == 0 ||
                        m->SuppressProbes - NonZeroTime(m->timenow + probedelay) < 0)
                        m->SuppressProbes = NonZeroTime(m->timenow + probedelay);
 
+               // Include OWNER option in packets for 60 seconds after connecting to the network. Setting
+               // it here also handles the wake up case as the network link comes UP after waking causing
+               // us to reconnect to the network. If we do this as part of the wake up code, it is possible
+               // that the network link comes UP after 60 seconds and we never set the OWNER option
+               m->AnnounceOwner = NonZeroTime(m->timenow + 60 * mDNSPlatformOneSecond);
+               LogInfo("mDNS_RegisterInterface: Setting AnnounceOwner");
+
                for (q = m->Questions; q; q=q->next)                                                            // Scan our list of questions
                        if (mDNSOpaque16IsZero(q->TargetQID))
                                if (!q->InterfaceID || q->InterfaceID == set->InterfaceID)              // If non-specific Q, or Q on this specific interface,
@@ -8538,12 +9558,7 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s
 mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping)
        {
        NetworkInterfaceInfo **p = &m->HostInterfaces;
-       
        mDNSBool revalidate = mDNSfalse;
-       // If this platform has the "phantom interfaces" known bug (e.g. Jaguar), we have to revalidate records every
-       // time an interface goes away. Otherwise, when you disconnect the Ethernet cable, the system reports that it
-       // still has an IPv6 address, and if we don't revalidate those records don't get deleted in a timely fashion.
-       if (m->KnownBugs & mDNS_KnownBug_PhantomInterfaces) revalidate = mDNStrue;
 
        mDNS_Lock(m);
 
@@ -8744,10 +9759,13 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr,
        const domainlabel *const name, const domainname *const type, const domainname *const domain,
        const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen,
        AuthRecord *SubTypes, mDNSu32 NumSubTypes,
-       const mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context)
+       mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context, mDNSu32 flags)
        {
        mStatus err;
        mDNSu32 i;
+       mDNSu32 hostTTL;
+       AuthRecType artype;
+       mDNSu8 recordType = (flags & regFlagKnownUnique) ? kDNSRecordTypeKnownUnique : kDNSRecordTypeUnique;
 
        sr->ServiceCallback = Callback;
        sr->ServiceContext  = Context;
@@ -8757,16 +9775,31 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr,
        sr->NumSubTypes     = NumSubTypes;
        sr->SubTypes        = SubTypes;
        
+       if (InterfaceID == mDNSInterface_LocalOnly)
+               artype = AuthRecordLocalOnly;
+       else if (InterfaceID == mDNSInterface_P2P)
+               artype = AuthRecordP2P;
+       else if ((InterfaceID == mDNSInterface_Any) && (flags & regFlagIncludeP2P))
+               artype = AuthRecordAnyIncludeP2P;
+       else
+               artype = AuthRecordAny;
+
        // Initialize the AuthRecord objects to sane values
        // Need to initialize everything correctly *before* making the decision whether to do a RegisterNoSuchService and bail out
-       mDNS_SetupResourceRecord(&sr->RR_ADV, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeAdvisory, ServiceCallback, sr);
-       mDNS_SetupResourceRecord(&sr->RR_PTR, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared,   ServiceCallback, sr);
-       mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique,   ServiceCallback, sr);
-       mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, InterfaceID, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnique,   ServiceCallback, sr);
+       mDNS_SetupResourceRecord(&sr->RR_ADV, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeAdvisory, artype, ServiceCallback, sr);
+       mDNS_SetupResourceRecord(&sr->RR_PTR, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared,   artype, ServiceCallback, sr);
+
+       if (SameDomainName(type, (const domainname *) "\x4" "_ubd" "\x4" "_tcp"))
+               hostTTL = kHostNameSmallTTL;
+       else
+               hostTTL = kHostNameTTL;
+
+       mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, InterfaceID, kDNSType_SRV, hostTTL, recordType, artype, ServiceCallback, sr);
+       mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, InterfaceID, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnique, artype, ServiceCallback, sr);
 
        // If port number is zero, that means the client is really trying to do a RegisterNoSuchService
        if (mDNSIPPortIsZero(port))
-               return(mDNS_RegisterNoSuchService(m, &sr->RR_SRV, name, type, domain, mDNSNULL, mDNSInterface_Any, NSSCallback, sr));
+               return(mDNS_RegisterNoSuchService(m, &sr->RR_SRV, name, type, domain, mDNSNULL, InterfaceID, NSSCallback, sr, (flags & regFlagIncludeP2P)));
 
        // If the client is registering an oversized TXT record,
        // it is the client's responsibility to alloate a ServiceRecordSet structure that is large enough for it
@@ -8801,7 +9834,7 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr,
                AssignDomainName(&st, sr->SubTypes[i].resrec.name);
                st.c[1+st.c[0]] = 0;                    // Only want the first label, not the whole FQDN (particularly for mDNS_RenameAndReregisterService())
                AppendDomainName(&st, type);
-               mDNS_SetupResourceRecord(&sr->SubTypes[i], mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, ServiceCallback, sr);
+               mDNS_SetupResourceRecord(&sr->SubTypes[i], mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, artype, ServiceCallback, sr);
                if (ConstructServiceName(&sr->SubTypes[i].namestorage, mDNSNULL, &st, domain) == mDNSNULL) return(mStatus_BadParamErr);
                AssignDomainName(&sr->SubTypes[i].resrec.rdata->u.name, &sr->RR_SRV.namestorage);
                sr->SubTypes[i].Additional1 = &sr->RR_SRV;
@@ -8834,6 +9867,14 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr,
        // that if the SRV cannot find a target, rest of the records that belong to this service
        // will not be activated.
        err = mDNS_Register_internal(m, &sr->RR_SRV);
+       // If we can't register the SRV record due to errors, bail out. It has not been inserted in
+       // any list and hence no need to deregister. We could probably do similar checks for other
+       // records below and bail out. For now, this seems to be sufficient to address rdar://9304275
+       if (err)
+               {
+               mDNS_Unlock(m);
+               return err;
+               }
        if (!err) err = mDNS_Register_internal(m, &sr->RR_TXT);
        // We register the RR_PTR last, because we want to be sure that in the event of a forced call to
        // mDNS_StartExit, the RR_PTR will be the last one to be forcibly deregistered, since that is what triggers
@@ -8851,14 +9892,25 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr,
        }
 
 mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr,
-       ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl)
+       ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl,  mDNSu32 includeP2P)
        {
        ExtraResourceRecord **e;
        mStatus status;
+       AuthRecType artype;
+       mDNSInterfaceID InterfaceID = sr->RR_PTR.resrec.InterfaceID;
+
+       if (InterfaceID == mDNSInterface_LocalOnly)
+               artype = AuthRecordLocalOnly;
+       if (InterfaceID == mDNSInterface_P2P)
+               artype = AuthRecordP2P;
+       else if ((InterfaceID == mDNSInterface_Any) && includeP2P)
+               artype = AuthRecordAnyIncludeP2P;
+       else
+               artype = AuthRecordAny;
 
        extra->next = mDNSNULL;
        mDNS_SetupResourceRecord(&extra->r, rdata, sr->RR_PTR.resrec.InterfaceID,
-               extra->r.resrec.rrtype, ttl, kDNSRecordTypeUnique, ServiceCallback, sr);
+               extra->r.resrec.rrtype, ttl, kDNSRecordTypeUnique, artype, ServiceCallback, sr);
        AssignDomainName(&extra->r.namestorage, sr->RR_SRV.resrec.name);
        
        mDNS_Lock(m);
@@ -8930,7 +9982,7 @@ mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordS
        err = mDNS_RegisterService(m, sr, newname, &type, &domain,
                host, sr->RR_SRV.resrec.rdata->u.srv.port, sr->RR_TXT.resrec.rdata->u.txt.c, sr->RR_TXT.resrec.rdlength,
                sr->SubTypes, sr->NumSubTypes,
-               sr->RR_PTR.resrec.InterfaceID, sr->ServiceCallback, sr->ServiceContext);
+               sr->RR_PTR.resrec.InterfaceID, sr->ServiceCallback, sr->ServiceContext, 0);
 
        // mDNS_RegisterService() just reset sr->Extras to NULL.
        // Fortunately we already grabbed ourselves a copy of this pointer (above), so we can now run
@@ -8939,7 +9991,7 @@ mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordS
                {
                ExtraResourceRecord *e = extras;
                extras = extras->next;
-               err = mDNS_AddRecordToService(m, sr, e, e->r.resrec.rdata, e->r.resrec.rroriginalttl);
+               err = mDNS_AddRecordToService(m, sr, e, e->r.resrec.rdata, e->r.resrec.rroriginalttl, 0);
                }
        
        return(err);
@@ -9016,9 +10068,20 @@ mDNSexport mStatus mDNS_DeregisterService_drt(mDNS *const m, ServiceRecordSet *s
 mDNSexport mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const rr,
        const domainlabel *const name, const domainname *const type, const domainname *const domain,
        const domainname *const host,
-       const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context)
+       const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context, mDNSBool includeP2P)
        {
-       mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique, Callback, Context);
+       AuthRecType artype;
+
+       if (InterfaceID == mDNSInterface_LocalOnly)
+               artype = AuthRecordLocalOnly;
+       else if (InterfaceID == mDNSInterface_P2P)
+               artype = AuthRecordP2P;
+       else if ((InterfaceID == mDNSInterface_Any) && includeP2P)
+               artype = AuthRecordAnyIncludeP2P;
+       else
+               artype = AuthRecordAny;
+
+       mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique, artype, Callback, Context);
        if (ConstructServiceName(&rr->namestorage, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr);
        rr->resrec.rdata->u.srv.priority    = 0;
        rr->resrec.rdata->u.srv.weight      = 0;
@@ -9031,7 +10094,15 @@ mDNSexport mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const r
 mDNSexport mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr,
        mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname)
        {
-       mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, mDNSNULL, mDNSNULL);
+       AuthRecType artype;
+
+       if (InterfaceID == mDNSInterface_LocalOnly)
+               artype = AuthRecordLocalOnly;
+       else if (InterfaceID == mDNSInterface_P2P)
+               artype = AuthRecordP2P;
+       else
+               artype = AuthRecordAny;
+       mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, artype, mDNSNULL, mDNSNULL);
        if (!MakeDomainNameFromDNSNameString(&rr->namestorage, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr);
        if (!MakeDomainNameFromDNSNameString(&rr->resrec.rdata->u.name, domname))                 return(mStatus_BadParamErr);
        return(mDNS_Register(m, rr));
@@ -9454,7 +10525,7 @@ mDNSlocal void SleepProxyServerCallback(mDNS *const m, ServiceRecordSet *const s
                                        (mDNSu8 *)"", 1,                                                        // TXT data, length
                                        mDNSNULL, 0,                                                            // Subtypes (none)
                                        mDNSInterface_Any,                                                      // Interface ID
-                                       SleepProxyServerCallback, mDNSNULL);            // Callback and context
+                                       SleepProxyServerCallback, mDNSNULL, 0);         // Callback, context, flags
                                }
                        LogSPS("Sleep Proxy Server %#s %s", srs->RR_SRV.resrec.name->c, m->SPSState ? "started" : "stopped");
                        }
@@ -9573,6 +10644,7 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p,
        m->NextScheduledResponse   = timenow + 0x78000000;
        m->NextScheduledNATOp      = timenow + 0x78000000;
        m->NextScheduledSPS        = timenow + 0x78000000;
+       m->NextScheduledStopTime   = timenow + 0x78000000;
        m->RandomQueryDelay        = 0;
        m->RandomReconfirmDelay    = 0;
        m->PktNum                  = 0;
@@ -9580,7 +10652,6 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p,
        m->SleepState              = SleepState_Awake;
        m->SleepSeqNum             = 0;
        m->SystemWakeOnLANEnabled  = mDNSfalse;
-       m->SentSleepProxyRegistration = mDNSfalse;
        m->AnnounceOwner           = NonZeroTime(timenow + 60 * mDNSPlatformOneSecond);
        m->DelaySleep              = 0;
        m->SleepLimit              = 0;
@@ -9591,6 +10662,7 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p,
        m->CurrentQuestion         = mDNSNULL;
        m->LocalOnlyQuestions      = mDNSNULL;
        m->NewLocalOnlyQuestions   = mDNSNULL;
+       m->RestartQuestion             = mDNSNULL;
        m->rrcache_size            = 0;
        m->rrcache_totalused       = 0;
        m->rrcache_active          = 0;
@@ -9604,6 +10676,10 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p,
                }
 
        mDNS_GrowCache_internal(m, rrcachestorage, rrcachesize);
+       m->rrauth.rrauth_free            = mDNSNULL;
+
+       for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+               m->rrauth.rrauth_hash[slot] = mDNSNULL;
 
        // Fields below only required for mDNS Responder...
        m->hostlabel.c[0]          = 0;
@@ -9614,6 +10690,7 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p,
        m->ResourceRecords         = mDNSNULL;
        m->DuplicateRecords        = mDNSNULL;
        m->NewLocalRecords         = mDNSNULL;
+       m->NewLocalOnlyRecords     = mDNSfalse;
        m->CurrentRecord           = mDNSNULL;
        m->HostInterfaces          = mDNSNULL;
        m->ProbeFailTime           = 0;
@@ -9948,16 +11025,16 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m)
        DNSServer   *ptr, **p = &m->DNSServers;
        const DNSServer *oldServers = m->DNSServers;
        DNSQuestion *q;
+       McastResolver *mr, **mres = &m->McastResolvers;
        
        debugf("uDNS_SetupDNSConfig: entry");
 
        // Let the platform layer get the current DNS information
-       // The m->StartWABQueries boolean is so that we lazily get the search domain list only on-demand
-       // and start the domain enumeration queries. (no need to hit the network with domain enumeration
-       // queries until we actually need that information).
+       // The m->StartWABQueries is set when we get the first domain enumeration query (no need to hit the network
+       // with domain enumeration queries until we actually need that information). Even if it is not set, we still
+       // need to setup the search domains so that we can append them to queries that need them.
 
-       uDNS_SetupSearchDomains(m, m->StartWABQueries ? (UDNS_START_WAB_QUERY | UDNS_START_CF_QUERY) :
-               (UDNS_START_CF_QUERY));
+       uDNS_SetupSearchDomains(m, m->StartWABQueries ? UDNS_START_WAB_QUERY : 0);
 
        mDNS_Lock(m);
 
@@ -9967,8 +11044,34 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m)
                ptr->flags |= DNSServer_FlagDelete;
                }
 
+       // We handle the mcast resolvers here itself as mDNSPlatformSetDNSConfig looks at
+       // mcast resolvers. Today we get both mcast and ucast configuration using the same
+       // API
+       for (mr = m->McastResolvers; mr; mr = mr->next)
+               mr->flags |= McastResolver_FlagDelete;
+
        mDNSPlatformSetDNSConfig(m, mDNStrue, mDNSfalse, &fqdn, mDNSNULL, mDNSNULL);
 
+       // For now, we just delete the mcast resolvers. We don't deal with cache or
+       // questions here. Neither question nor cache point to mcast resolvers. Questions
+       // do inherit the timeout values from mcast resolvers. But we don't bother
+       // affecting them as they never change.
+       while (*mres)
+               {
+               if (((*mres)->flags & DNSServer_FlagDelete) != 0)
+                       {
+                       mr = *mres;
+                       *mres = (*mres)->next;
+                       debugf("uDNS_SetupDNSConfig: Deleting mcast resolver %##s", mr, mr->domain.c);
+                       mDNSPlatformMemFree(mr);
+                       }
+               else
+                       {
+                       (*mres)->flags &= ~McastResolver_FlagNew;
+                       mres = &(*mres)->next;
+                       }
+               }
+
        // Mark the records to be flushed that match a new resolver. We need to do this before
        // we walk the questions below where we change the DNSServer pointer of the cache
        // record
@@ -9994,7 +11097,10 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m)
                                mDNS_PurgeCacheResourceRecord(m, cr);
                                }
                        else
+                               {
+                               LogInfo("uDNS_SetupDNSConfig: Purging/Reconfirming Resourcerecord %s", CRDisplayString(m, cr));
                                PurgeOrReconfirmCacheRecord(m, cr, ptr, mDNSfalse);
+                               }
                        }
                }
        // Update our qDNSServer pointers before we go and free the DNSServer object memory
index 26aa0a87e38bc6e54086a6faff6d52740a9a8a0a..3d6c12a4ec300f5e8d6f0ac1a94c3a702abb8eb6 100755 (executable)
@@ -348,6 +348,7 @@ enum
        mStatus_NATPortMappingDisabled    = -65565,     // NAT supports NAT-PMP or UPnP but it's disabled by the administrator
        mStatus_NoRouter                  = -65566,
        mStatus_PollingMode               = -65567,
+       mStatus_Timeout                   = -65568,
        // -65568 to -65786 currently unused; available for allocation
 
        // tcp connection status
@@ -406,6 +407,13 @@ typedef struct { mDNSu8 c[256]; } UTF8str255;              // Null-terminated C string
 #define kStandardTTL (3600UL * 100 / 80)
 #define kHostNameTTL 120UL
 
+// Some applications want to register their SRV records with a lower ttl so that in case the server
+// using a dynamic port number restarts, the clients will not have stale information for more than
+// 10 seconds
+
+#define kHostNameSmallTTL 10UL
+
+
 // Multicast DNS uses announcements (gratuitous responses) to update peer caches.
 // This means it is feasible to use relatively larger TTL values than we might otherwise
 // use, because we have a cache coherency protocol to keep the peer caches up to date.
@@ -424,6 +432,7 @@ typedef struct AuthRecord_struct AuthRecord;
 typedef struct ServiceRecordSet_struct ServiceRecordSet;
 typedef struct CacheRecord_struct CacheRecord;
 typedef struct CacheGroup_struct CacheGroup;
+typedef struct AuthGroup_struct AuthGroup;
 typedef struct DNSQuestion_struct DNSQuestion;
 typedef struct ZoneData_struct ZoneData;
 typedef struct mDNS_struct mDNS;
@@ -1036,6 +1045,21 @@ enum
        DNSServer_FlagNew    = 2
        };
 
+enum
+       {
+       McastResolver_FlagDelete = 1,
+       McastResolver_FlagNew    = 2
+       };
+
+typedef struct McastResolver
+       {
+       struct McastResolver *next;
+       mDNSInterfaceID interface;
+       mDNSu32         flags;          // Set when we're planning to delete this from the list
+       domainname      domain;
+       mDNSu32         timeout;        // timeout value for questions
+       } McastResolver;
+
 typedef struct DNSServer
        {
        struct DNSServer *next;
@@ -1050,6 +1074,7 @@ typedef struct DNSServer
        mDNSs32                 penaltyTime; // amount of time this server is penalized
        mDNSBool                scoped;         // interface should be matched against question only
                                                                // if scoped is set
+       mDNSu32                 timeout;        // timeout value for questions
        } DNSServer;
 
 typedef struct                                                 // Size is 36 bytes when compiling for 32-bit; 48 when compiling for 64-bit
@@ -1108,6 +1133,45 @@ typedef enum
        mergeState_DontMerge = 1  // Set on fatal error conditions to disable merging
        } mergeState_t;
 
+struct AuthGroup_struct                                // Header object for a list of AuthRecords with the same name
+       {
+       AuthGroup      *next;                           // Next AuthGroup object in this hash table bucket
+       mDNSu32         namehash;                       // Name-based (i.e. case insensitive) hash of name
+       AuthRecord     *members;                        // List of CacheRecords with this same name
+       AuthRecord    **rrauth_tail;            // Tail end of that list
+       domainname     *name;                           // Common name for all AuthRecords in this list
+       AuthRecord     *NewLocalOnlyRecords;
+       // Size to here is 20 bytes when compiling 32-bit; 40 bytes when compiling 64-bit
+       mDNSu8          namestorage[InlineCacheGroupNameSize];
+       };
+
+#define AUTH_HASH_SLOTS 499
+#define FORALL_AUTHRECORDS(SLOT,AG,AR)                                 \
+       for ((SLOT) = 0; (SLOT) < AUTH_HASH_SLOTS; (SLOT)++)            \
+               for ((AG)=m->rrauth.rrauth_hash[(SLOT)]; (AG); (AG)=(AG)->next) \
+                       for ((AR) = (AG)->members; (AR); (AR)=(AR)->next)
+
+typedef union AuthEntity_union AuthEntity;
+union AuthEntity_union { AuthEntity *next; AuthGroup ag; };
+typedef struct {
+       mDNSu32 rrauth_size;                            // Total number of available auth entries
+       mDNSu32 rrauth_totalused;                       // Number of auth entries currently occupied
+       mDNSu32 rrauth_report;
+       mDNSu8  rrauth_lock;                            // For debugging: Set at times when these lists may not be modified
+       AuthEntity *rrauth_free;
+       AuthGroup *rrauth_hash[AUTH_HASH_SLOTS];
+}AuthHash;
+
+// AuthRecordAny includes mDNSInterface_Any and interface specific auth records (anything
+// other than P2P or LocalOnly)
+typedef enum 
+       {
+       AuthRecordAny,                          // registered for *Any, NOT including P2P interfaces
+       AuthRecordAnyIncludeP2P,        // registered for *Any, including P2P interfaces
+       AuthRecordLocalOnly, 
+       AuthRecordP2P                           // discovered over D2D/P2P framework
+       } AuthRecType;
+
 struct AuthRecord_struct
        {
        // For examples of how to set up this structure for use in mDNS_Register(),
@@ -1134,6 +1198,7 @@ struct AuthRecord_struct
        mDNSAddr        AddressProxy;           // For reverse-mapping Sleep Proxy PTR records, address in question
        mDNSs32         TimeRcvd;                       // In platform time units
        mDNSs32         TimeExpire;                     // In platform time units
+       AuthRecType     ARType;             // LocalOnly, P2P or Normal ?
 
        // Field Group 3: Transient state for Authoritative Records
        mDNSu8          Acknowledged;           // Set if we've given the success callback to the client
@@ -1218,12 +1283,21 @@ struct AuthRecord_struct
 #define Question_uDNS(Q)   ((Q)->InterfaceID == mDNSInterface_Unicast || \
        ((Q)->InterfaceID != mDNSInterface_LocalOnly && (Q)->InterfaceID != mDNSInterface_P2P && !(Q)->ForceMCast && !IsLocalDomain(&(Q)->qname)))
 
+#define RRLocalOnly(rr) ((rr)->ARType == AuthRecordLocalOnly || (rr)->ARType == AuthRecordP2P)
+
+#define RRAny(rr) ((rr)->ARType == AuthRecordAny || (rr)->ARType == AuthRecordAnyIncludeP2P)
+
 // Question (A or AAAA) that is suppressed currently because IPv4 or IPv6 address
 // is not available locally for A or AAAA question respectively
 #define QuerySuppressed(Q) ((Q)->SuppressUnusable && (Q)->SuppressQuery)
 
 #define PrivateQuery(Q) ((Q)->AuthInfo && (Q)->AuthInfo->AutoTunnel)
 
+// Normally we always lookup the cache and /etc/hosts before sending the query on the wire. For single label
+// queries (A and AAAA) that are unqualified (indicated by AppendSearchDomains), we want to append search
+// domains before we try them as such
+#define ApplySearchDomainsFirst(q) ((q)->AppendSearchDomains && (CountLabels(&((q)->qname))) == 1)
+
 // Wrapper struct for Auth Records for higher-level code that cannot use the AuthRecord's ->next pointer field
 typedef struct ARListElem
        {
@@ -1242,6 +1316,7 @@ struct CacheGroup_struct                          // Header object for a list of CacheRecords with the
        mDNSu8          namestorage[InlineCacheGroupNameSize];
        };
 
+
 struct CacheRecord_struct
        {
        CacheRecord    *next;                           // Next in list; first element of structure for efficiency reasons
@@ -1401,7 +1476,7 @@ typedef struct DomainAuthInfo
        {
        struct DomainAuthInfo *next;
        mDNSs32          deltime;                               // If we're planning to delete this DomainAuthInfo, the time we want it deleted
-       mDNSBool         AutoTunnel;
+       const char*      AutoTunnel;            // If NULL, this is not an AutoTunnel DAI. Otherwise, this is prepended to the IPSec identifier
        AuthRecord       AutoTunnelHostRecord;  // User-visible hostname; used as SRV target for AutoTunnel services
        AuthRecord       AutoTunnelTarget;              // Opaque hostname of tunnel endpoint; used as SRV target for AutoTunnelService record
        AuthRecord       AutoTunnelDeviceInfo;  // Device info of tunnel endpoint
@@ -1410,6 +1485,8 @@ typedef struct DomainAuthInfo
        NATTraversalInfo AutoTunnelNAT;
        domainname       domain;
        domainname       keyname;
+       domainname       hostname;
+       mDNSIPPort       port;
        char             b64keydata[32];
        mDNSu8           keydata_ipad[HMAC_LEN];        // padded key for inner hash rounds
        mDNSu8           keydata_opad[HMAC_LEN];        // padded key for outer hash rounds
@@ -1452,7 +1529,10 @@ struct DNSQuestion_struct
        mDNSs32               LastQTxTime;              // Last time this Q was sent on one (but not necessarily all) interfaces
        mDNSu32               CNAMEReferrals;   // Count of how many CNAME redirections we've done
        mDNSBool              SuppressQuery;    // This query should be suppressed and not sent on the wire 
+       mDNSu8                LOAddressAnswers; // Number of answers from the local only auth records that are
+                                                   // answering A, AAAA and CNAME (/etc/hosts)
        mDNSu8                WakeOnResolveCount; // Number of wakes that should be sent on resolve
+       mDNSs32               StopTime;                 // Time this question should be stopped by giving them a negative answer
 
        // Wide Area fields. These are used internally by the uDNS core
        UDPSocket            *LocalSocket;
@@ -1461,7 +1541,7 @@ struct DNSQuestion_struct
        mDNSOpaque64          validDNSServers;  // Valid DNSServers for this question
        mDNSu16              noServerResponse;  // At least one server did not respond.
        mDNSu16              triedAllServersOnce; // Tried all DNS servers once
-       mDNSu8                unansweredQueries;// The number of unanswered queries to this server
+       mDNSu8               unansweredQueries;// The number of unanswered queries to this server
 
        ZoneData             *nta;                              // Used for getting zone data for private or LLQ query
        mDNSAddr              servAddr;                 // Address and port learned from _dns-llq, _dns-llq-tls or _dns-query-tls SRV query
@@ -1494,7 +1574,13 @@ struct DNSQuestion_struct
        mDNSBool              ForceMCast;               // Set by client to force mDNS query, even for apparently uDNS names
        mDNSBool              ReturnIntermed;   // Set by client to request callbacks for intermediate CNAME/NXDOMAIN results
        mDNSBool              SuppressUnusable; // Set by client to suppress unusable queries to be sent on the wire
-       mDNSBool              WakeOnResolve;    // Send wakeup on resolve
+       mDNSBool              RetryWithSearchDomains;   // Retry with search domains if there is no entry in the cache or AuthRecords
+       mDNSu8                TimeoutQuestion; // Timeout this question if there is no reply in configured time
+       mDNSu8                WakeOnResolve; // Send wakeup on resolve
+       mDNSs8                SearchListIndex;  // Index into SearchList; Used by the client layer but not touched by core
+       mDNSs8                AppendSearchDomains; // Search domains can be appended for this query
+       mDNSs8                AppendLocalSearchDomains; // Search domains ending in .local can be appended for this query
+       domainname           *qnameOrig;       // Copy of the original question name if it is not fully qualified
        mDNSQuestionCallback *QuestionCallback;
        void                 *QuestionContext;
        };
@@ -1557,6 +1643,7 @@ struct ZoneData_struct
 
 extern ZoneData *StartGetZoneData(mDNS *const m, const domainname *const name, const ZoneService target, ZoneDataCallback callback, void *callbackInfo);
 extern void CancelGetZoneData(mDNS *const m, ZoneData *nta);
+extern mDNSBool IsGetZoneDataQuestion(DNSQuestion *q);
 
 typedef struct DNameListElem
        {
@@ -1575,6 +1662,7 @@ typedef struct DNameListElem
 typedef struct ClientTunnel
        {
        struct ClientTunnel *next;
+       const char *prefix;
        domainname dstname;
        mDNSBool   MarkedForDeletion;
        mDNSv6Addr loc_inner;
@@ -1636,23 +1724,23 @@ struct NetworkInterfaceInfo_struct
        mDNSu8          Advertise;                      // False if you are only searching on this interface
        mDNSu8          McastTxRx;                      // Send/Receive multicast on this { InterfaceID, address family } ?
        mDNSu8          NetWake;                        // Set if Wake-On-Magic-Packet is enabled on this interface
+       mDNSu8          Loopback;                       // Set if this is the loopback interface
        };
 
 #define SLE_DELETE              0x00000001
 #define SLE_WAB_QUERY_STARTED   0x00000002
-#define SLE_CF_QUERY_STARTED    0x00000004
 
 typedef struct SearchListElem
        {
        struct SearchListElem *next;
        domainname domain;
-       int flag;               // -1 means delete, 0 means unchanged, +1 means newly added
+       int flag;
+       mDNSInterfaceID InterfaceID;
        DNSQuestion BrowseQ;
        DNSQuestion DefBrowseQ;
        DNSQuestion AutomaticBrowseQ;
        DNSQuestion RegisterQ;
        DNSQuestion DefRegisterQ;
-       DNSQuestion CfQ;
        int     numCfAnswers;
        ARListElem *AuthRecs;
        } SearchListElem;
@@ -1676,9 +1764,8 @@ typedef void mDNSCallback(mDNS *const m, mStatus result);
 
 enum           // Bit flags -- i.e. values should be 1, 2, 4, 8, etc.
        {
-       mDNS_KnownBug_PhantomInterfaces = 1,
-       mDNS_KnownBug_LimitedIPv6       = 2,
-       mDNS_KnownBug_LossySyslog       = 4             // <rdar://problem/6561888>
+       mDNS_KnownBug_LimitedIPv6       = 1,
+       mDNS_KnownBug_LossySyslog       = 2             // <rdar://problem/6561888>
        };
 
 enum
@@ -1747,12 +1834,15 @@ struct mDNS_struct
        mDNSs32  NextScheduledSPRetry;          // Time next sleep proxy registration action is required.
                                                                                // Only valid if SleepLimit is nonzero and DelaySleep is zero.
 
+       mDNSs32 NextScheduledStopTime;      // Next time to stop a question
+
        // These fields only required for mDNS Searcher...
        DNSQuestion *Questions;                         // List of all registered questions, active and inactive
        DNSQuestion *NewQuestions;                      // Fresh questions not yet answered from cache
        DNSQuestion *CurrentQuestion;           // Next question about to be examined in AnswerLocalQuestions()
        DNSQuestion *LocalOnlyQuestions;        // Questions with InterfaceID set to mDNSInterface_LocalOnly or mDNSInterface_P2P
        DNSQuestion *NewLocalOnlyQuestions;     // Fresh local-only or P2P questions not yet answered
+       DNSQuestion *RestartQuestion;           // Questions that are being restarted (stop followed by start)
        mDNSu32 rrcache_size;                           // Total number of available cache entries
        mDNSu32 rrcache_totalused;                      // Number of cache entries currently occupied
        mDNSu32 rrcache_active;                         // Number of cache entries currently occupied by records that answer active questions
@@ -1761,6 +1851,8 @@ struct mDNS_struct
        CacheGroup *rrcache_hash[CACHE_HASH_SLOTS];
        mDNSs32  rrcache_nextcheck[CACHE_HASH_SLOTS];
 
+       AuthHash rrauth;
+
        // Fields below only required for mDNS Responder...
        domainlabel nicelabel;                          // Rich text label encoded using canonically precomposed UTF-8
        domainlabel hostlabel;                          // Conforms to RFC 1034 "letter-digit-hyphen" ARPANET host name rules
@@ -1770,8 +1862,9 @@ struct mDNS_struct
        AuthRecord  DeviceInfo;
        AuthRecord *ResourceRecords;
        AuthRecord *DuplicateRecords;           // Records currently 'on hold' because they are duplicates of existing records
-       AuthRecord *NewLocalRecords;            // Fresh AuthRecords (both local-only and public) not yet delivered to our local-only questions
+       AuthRecord *NewLocalRecords;            // Fresh AuthRecords (public) not yet delivered to our local-only questions
        AuthRecord *CurrentRecord;                      // Next AuthRecord about to be examined
+       mDNSBool    NewLocalOnlyRecords;        // Fresh AuthRecords (local only) not yet delivered to our local questions
        NetworkInterfaceInfo *HostInterfaces;
        mDNSs32 ProbeFailTime;
        mDNSu32 NumFailedProbes;
@@ -1782,6 +1875,7 @@ struct mDNS_struct
        mDNSs32           NextSRVUpdate;        // Time to perform delayed update
 
        DNSServer        *DNSServers;           // list of DNS servers
+       McastResolver    *McastResolvers;       // list of Mcast Resolvers
 
        mDNSAddr          Router;
        mDNSAddr          AdvertisedV4;         // IPv4 address pointed to by hostname
@@ -1804,7 +1898,7 @@ struct mDNS_struct
        mDNSv6Addr        AutoTunnelRelayAddrOut;
        domainlabel       AutoTunnelLabel;              // Used to construct hostname for *IPv4* address of tunnel endpoints
 
-       mDNSBool          StartWABQueries;
+       mDNSBool          StartWABQueries;              // Start WAB queries for the purpose of domain enumeration
        mDNSBool          RegisterAutoTunnel6;
 
        // NAT-Traversal fields
@@ -2038,6 +2132,7 @@ extern mStatus mDNS_Init      (mDNS *const m, mDNS_PlatformSupport *const p,
 
 extern void    mDNS_ConfigChanged(mDNS *const m);
 extern void    mDNS_GrowCache (mDNS *const m, CacheEntity *storage, mDNSu32 numrecords);
+extern void    mDNS_GrowAuth (mDNS *const m, AuthEntity *storage, mDNSu32 numrecords);
 extern void    mDNS_StartExit (mDNS *const m);
 extern void    mDNS_FinalExit (mDNS *const m);
 #define mDNS_Close(m) do { mDNS_StartExit(m); mDNS_FinalExit(m); } while(0)
@@ -2109,14 +2204,21 @@ typedef enum { mDNS_Dereg_normal, mDNS_Dereg_rapid, mDNS_Dereg_conflict, mDNS_De
 // and the default domain in which to register in the case where the user has made no selection.
 
 extern void    mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID,
-               mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, mDNSRecordCallback Callback, void *Context);
+               mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, AuthRecType artype, mDNSRecordCallback Callback, void *Context);
+
+// mDNS_RegisterService() flags parameter bit definitions
+enum
+       {
+               regFlagIncludeP2P       = 0x1,  // include P2P interfaces when using mDNSInterface_Any
+               regFlagKnownUnique      = 0x2   // client guarantees that SRV and TXT record names are unique
+       };
 
 extern mStatus mDNS_RegisterService  (mDNS *const m, ServiceRecordSet *sr,
                const domainlabel *const name, const domainname *const type, const domainname *const domain,
                const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen,
                AuthRecord *SubTypes, mDNSu32 NumSubTypes,
-               const mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context);
-extern mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl);
+               mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context, mDNSu32 flags);
+extern mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl,  mDNSu32 includeP2P);
 extern mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, mDNSRecordCallback MemFreeCallback, void *Context);
 extern mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordSet *const sr, const domainlabel *newname);
 extern mStatus mDNS_DeregisterService_drt(mDNS *const m, ServiceRecordSet *sr, mDNS_Dereg_type drt);
@@ -2125,7 +2227,7 @@ extern mStatus mDNS_DeregisterService_drt(mDNS *const m, ServiceRecordSet *sr, m
 extern mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const rr,
                const domainlabel *const name, const domainname *const type, const domainname *const domain,
                const domainname *const host,
-               const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context);
+               const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context, mDNSBool includeP2P);
 #define        mDNS_DeregisterNoSuchService mDNS_Deregister
 
 extern void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID InterfaceID, const domainname *const name,
@@ -2163,7 +2265,7 @@ extern mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID I
 
 extern DNSServer *GetServerForName(mDNS *m, const domainname *name, mDNSInterfaceID InterfaceID);
 extern DNSServer *GetServerForQuestion(mDNS *m, DNSQuestion *question);
-extern void SetValidDNSServers(mDNS *m, DNSQuestion *question);
+extern mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question);
 
 // ***************************************************************************
 #if 0
@@ -2325,9 +2427,6 @@ extern mDNSBool mDNSv4AddrIsRFC1918(mDNSv4Addr *addr);  // returns true for RFC1
 #define mDNSv4AddressIsLoopback(X) ((X)->b[0] == 127 && (X)->b[1] == 0 && (X)->b[2] == 0 && (X)->b[3] == 1)
 #define mDNSv6AddressIsLoopback(X) ((((X)->l[0] | (X)->l[1] | (X)->l[2]) == 0) && ((X)->b[12] == 0 && (X)->b[13] == 0 && (X)->b[14] == 0 && (X)->b[15] == 1))
 
-#define mDNSAddressIsLoopback(X)  (                                                    \
-       ((X)->type == mDNSAddrType_IPv4) ? mDNSv4AddressIsLoopback(&(X)->ip.v4) :          \
-       ((X)->type == mDNSAddrType_IPv6) ? mDNSv6AddressIsLoopback(&(X)->ip.v6) : mDNSfalse)
 // ***************************************************************************
 #if 0
 #pragma mark -
@@ -2341,10 +2440,11 @@ extern mDNSBool mDNSv4AddrIsRFC1918(mDNSv4Addr *addr);  // returns true for RFC1
 // domain name format.  The shared secret must be a null-terminated base64 encoded string.  A minimum size of
 // 16 bytes (128 bits) is recommended for an MD5 hash as per RFC 2485.
 // Calling this routine multiple times for a zone replaces previously entered values.  Call with a NULL key
-// to dissable authentication for the zone.
+// to disable authentication for the zone.  A non-NULL autoTunnelPrefix means this is an AutoTunnel domain,
+// and the value is prepended to the IPSec identifier (used for key lookup)
 
 extern mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info,
-       const domainname *domain, const domainname *keyname, const char *b64keydata, mDNSBool AutoTunnel);
+       const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, const char *autoTunnelPrefix);
 
 extern void RecreateNATMappings(mDNS *const m);
 
@@ -2368,13 +2468,15 @@ extern void RecreateNATMappings(mDNS *const m);
 extern void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext);
 extern void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn);
 extern void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr,  const mDNSAddr *v6addr, const mDNSAddr *router);
-extern DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped);
+extern DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout);
 extern void PenalizeDNSServer(mDNS *const m, DNSQuestion *q);
-extern void mDNS_AddSearchDomain(const domainname *const domain);
+extern void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID);
+
+extern McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, mDNSu32 timeout);
 
 // We use ((void *)0) here instead of mDNSNULL to avoid compile warnings on gcc 4.2
-#define mDNS_AddSearchDomain_CString(X) \
-       do { domainname d__; if (((X) != (void*)0) && MakeDomainNameFromDNSNameString(&d__, (X)) && d__.c[0]) mDNS_AddSearchDomain(&d__); } while(0)
+#define mDNS_AddSearchDomain_CString(X, I) \
+       do { domainname d__; if (((X) != (void*)0) && MakeDomainNameFromDNSNameString(&d__, (X)) && d__.c[0]) mDNS_AddSearchDomain(&d__, I); } while(0)
 
 // Routines called by the core, exported by DNSDigest.c
 
@@ -2489,7 +2591,7 @@ mDNSexport void mDNSASLLog(uuid_t *uuid, const char *subdomain, const char *resu
 // and interface indexes in order to support the DNS-SD API. If your target platform does not support
 // multiple interfaces and/or does not support the DNS-SD API, these functions can be empty.
 extern mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(mDNS *const m, mDNSu32 ifindex);
-extern mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id);
+extern mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id, mDNSBool suppressNetworkChange);
 
 // Every platform support module must provide the following functions if it is to support unicast DNS
 // and Dynamic Update.
@@ -2542,8 +2644,9 @@ extern void       mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, m
 extern mStatus    mDNSPlatformGetPrimaryInterface(mDNS *const m, mDNSAddr *v4, mDNSAddr *v6, mDNSAddr *router);
 extern void       mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const dname, const mStatus status);
 
-extern void       mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep);
+extern void       mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason);
 extern void       mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID InterfaceID, char *EthAddr, char *IPAddr, int iteration);
+extern mDNSBool   mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf);
 
 #ifdef _LEGACY_NAT_TRAVERSAL_
 // Support for legacy NAT traversal protocols, implemented by the platform layer and callable by the core.
@@ -2599,7 +2702,11 @@ extern void     mDNSCoreInitComplete(mDNS *const m, mStatus result);
 extern void     mDNSCoreReceive(mDNS *const m, void *const msg, const mDNSu8 *const end,
                                                                const mDNSAddr *const srcaddr, const mDNSIPPort srcport,
                                                                const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID);
-extern void    mDNSCoreRestartQueries(mDNS *const m);
+extern void            mDNSCoreRestartQueries(mDNS *const m);
+typedef void    (*FlushCache)(mDNS *const m);
+typedef void    (*CallbackBeforeStartQuery)(mDNS *const m, void *context);
+extern void            mDNSCoreRestartAddressQueries(mDNS *const m, mDNSBool SearchDomainsChanged, FlushCache flushCacheRecords,
+                                                                                         CallbackBeforeStartQuery beforeQueryStart, void *context);
 extern mDNSBool mDNSCoreHaveAdvertisedMulticastServices(mDNS *const m);
 extern void     mDNSCoreMachineSleep(mDNS *const m, mDNSBool wake);
 extern mDNSBool mDNSCoreReadyForSleep(mDNS *m, mDNSs32 now);
@@ -2618,9 +2725,18 @@ extern void MakeNegativeCacheRecord(mDNS *const m, CacheRecord *const cr,
 extern void CompleteDeregistration(mDNS *const m, AuthRecord *rr);
 extern void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheRecord *const rr, const QC_result AddRecord);
 extern char *InterfaceNameForID(mDNS *const m, const mDNSInterfaceID InterfaceID);
-extern void DNSServerChangeForQuestion(mDNS *const m, DNSQuestion *q, DNSServer *new);
+extern void DNSServerChangeForQuestion(mDNS *const m, DNSQuestion *q, DNSServer *newServer);
 extern void ActivateUnicastRegistration(mDNS *const m, AuthRecord *const rr);
 extern void CheckSuppressUnusableQuestions(mDNS *const m);
+extern void RetrySearchDomainQuestions(mDNS *const m);
+
+// Used only in logging to restrict the number of /etc/hosts entries printed
+extern void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result);
+// exported for using the hash for /etc/hosts AuthRecords
+extern AuthGroup *AuthGroupForName(AuthHash *r, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name);
+extern AuthGroup *AuthGroupForRecord(AuthHash *r, const mDNSu32 slot, const ResourceRecord *const rr);
+extern AuthGroup *InsertAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr);
+extern AuthGroup *RemoveAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr);
 
 // For now this AutoTunnel stuff is specific to Mac OS X.
 // In the future, if there's demand, we may see if we can abstract it out cleanly into the platform layer
@@ -2631,7 +2747,6 @@ extern void SetupLocalAutoTunnelInterface_internal(mDNS *const m, mDNSBool servi
 extern void UpdateAutoTunnelDomainStatuses(const mDNS *const m);
 extern mStatus ActivateLocalProxy(mDNS *const m, char *ifname);
 extern void RemoveAutoTunnel6Record(mDNS *const m);
-extern void SetupConndConfigChanges(mDNS *const m);
 extern mDNSBool RecordReadyForSleep(mDNS *const m, AuthRecord *rr);
 #endif
 
@@ -2753,6 +2868,17 @@ extern mDNSBool RecordReadyForSleep(mDNS *const m, AuthRecord *rr);
 // 60 =   1 W
 // 90 =   1 kW
 
+typedef enum
+       {
+       mDNSSleepProxyMetric_Dedicated          = 20,
+       mDNSSleepProxyMetric_PrimaryHardware    = 30,
+       mDNSSleepProxyMetric_PrimarySoftware    = 40,
+       mDNSSleepProxyMetric_SecondaryHardware  = 50,
+       mDNSSleepProxyMetric_SecondarySoftware  = 60,
+       mDNSSleepProxyMetric_IncidentalHardware = 70,
+       mDNSSleepProxyMetric_IncidentalSoftware = 80
+       } mDNSSleepProxyMetric;
+
 extern void mDNSCoreBeSleepProxyServer_internal(mDNS *const m, mDNSu8 sps, mDNSu8 port, mDNSu8 marginalpower, mDNSu8 totpower);
 #define mDNSCoreBeSleepProxyServer(M,S,P,MP,TP) \
        do { mDNS_Lock(m); mDNSCoreBeSleepProxyServer_internal((M),(S),(P),(MP),(TP)); mDNS_Unlock(m); } while(0)
@@ -2813,17 +2939,17 @@ struct CompileTimeAssertionChecks_mDNS
        char sizecheck_AuthRecord          [(sizeof(AuthRecord)           <=  1208) ? 1 : -1];
        char sizecheck_CacheRecord         [(sizeof(CacheRecord)          <=   184) ? 1 : -1];
        char sizecheck_CacheGroup          [(sizeof(CacheGroup)           <=   184) ? 1 : -1];
-       char sizecheck_DNSQuestion         [(sizeof(DNSQuestion)          <=   762) ? 1 : -1];
-       char sizecheck_ZoneData            [(sizeof(ZoneData)             <=  1598) ? 1 : -1];
+       char sizecheck_DNSQuestion         [(sizeof(DNSQuestion)          <=   786) ? 1 : -1];
+       char sizecheck_ZoneData            [(sizeof(ZoneData)             <=  1624) ? 1 : -1];
        char sizecheck_NATTraversalInfo    [(sizeof(NATTraversalInfo)     <=   192) ? 1 : -1];
        char sizecheck_HostnameInfo        [(sizeof(HostnameInfo)         <=  3050) ? 1 : -1];
        char sizecheck_DNSServer           [(sizeof(DNSServer)            <=   320) ? 1 : -1];
-       char sizecheck_NetworkInterfaceInfo[(sizeof(NetworkInterfaceInfo) <=  6750) ? 1 : -1];
+       char sizecheck_NetworkInterfaceInfo[(sizeof(NetworkInterfaceInfo) <=  6850) ? 1 : -1];
        char sizecheck_ServiceRecordSet    [(sizeof(ServiceRecordSet)     <=  5500) ? 1 : -1];
-       char sizecheck_DomainAuthInfo      [(sizeof(DomainAuthInfo)       <=  7550) ? 1 : -1];
-       char sizecheck_ServiceInfoQuery    [(sizeof(ServiceInfoQuery)     <=  3090) ? 1 : -1];
+       char sizecheck_DomainAuthInfo      [(sizeof(DomainAuthInfo)       <=  7808) ? 1 : -1];
+       char sizecheck_ServiceInfoQuery    [(sizeof(ServiceInfoQuery)     <=  3200) ? 1 : -1];
 #if APPLE_OSX_mDNSResponder
-       char sizecheck_ClientTunnel        [(sizeof(ClientTunnel)         <=  1104) ? 1 : -1];
+       char sizecheck_ClientTunnel        [(sizeof(ClientTunnel)         <=  1148) ? 1 : -1];
 #endif
        };
 
index 5d0facfe3aa926eb027e1d9bdc1ad5b2d7bf78ff..513150bef08ad8fa6d513b2e7e46cedc31639665 100755 (executable)
@@ -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 <TargetConditionals.h>
+#endif
 #include "uDNS.h"
 
 #if(defined(_MSC_VER))
@@ -98,7 +101,7 @@ mDNSlocal void SetRecordRetry(mDNS *const m, AuthRecord *rr, mDNSu32 random)
 #pragma mark - Name Server List Management
 #endif
 
-mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped)
+mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout)
        {
        DNSServer **p = &m->DNSServers;
        DNSServer *tmp = mDNSNULL;
@@ -146,6 +149,7 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons
                        (*p)->flags     = DNSServer_FlagNew;
                        (*p)->teststate = /* DNSServer_Untested */ DNSServer_Passed;
                        (*p)->lasttest  = m->timenow - INIT_UCAST_POLL_INTERVAL;
+                       (*p)->timeout   = timeout;
                        AssignDomainName(&(*p)->domain, d);
                        (*p)->next = mDNSNULL;
                        }
@@ -345,17 +349,25 @@ mDNSexport DomainAuthInfo *GetAuthInfoForName(mDNS *m, const domainname *const n
 
 // MUST be called with the lock held
 mDNSexport mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info,
-       const domainname *domain, const domainname *keyname, const char *b64keydata, mDNSBool AutoTunnel)
+       const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, const char *autoTunnelPrefix)
        {
        DNSQuestion *q;
        DomainAuthInfo **p = &m->AuthInfoList;
        if (!info || !b64keydata) { LogMsg("mDNS_SetSecretForDomain: ERROR: info %p b64keydata %p", info, b64keydata); return(mStatus_BadParamErr); }
 
-       LogInfo("mDNS_SetSecretForDomain: domain %##s key %##s%s", domain->c, keyname->c, AutoTunnel ? " AutoTunnel" : "");
+       LogInfo("mDNS_SetSecretForDomain: domain %##s key %##s%s%s", domain->c, keyname->c, autoTunnelPrefix ? " prefix " : "", autoTunnelPrefix ? autoTunnelPrefix : "");
 
-       info->AutoTunnel = AutoTunnel;
+       info->AutoTunnel = autoTunnelPrefix;
        AssignDomainName(&info->domain,  domain);
        AssignDomainName(&info->keyname, keyname);
+       if (hostname)
+               AssignDomainName(&info->hostname, hostname);
+       else
+               info->hostname.c[0] = 0;
+       if (port)
+               info->port = *port;
+       else
+               info->port = zeroIPPort;
        mDNS_snprintf(info->b64keydata, sizeof(info->b64keydata), "%s", b64keydata);
 
        if (DNSDigest_ConstructHMACKeyfromBase64(info, b64keydata) < 0)
@@ -701,7 +713,7 @@ mDNSlocal mDNSu8 *putLLQ(DNSMessage *const msg, mDNSu8 *ptr, const DNSQuestion *
        // !!!KRS implement me
 
        // format opt rr (fields not specified are zero-valued)
-       mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
+       mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
        opt->rrclass    = NormalMaxDNSMessageData;
        opt->rdlength   = sizeof(rdataOPT);     // One option in this OPT record
        opt->rdestimate = sizeof(rdataOPT);
@@ -1575,7 +1587,12 @@ mDNSlocal mStatus GetZoneData_StartQuery(mDNS *const m, ZoneData *zd, mDNSu16 qt
        zd->question.ForceMCast          = mDNSfalse;
        zd->question.ReturnIntermed      = mDNStrue;
        zd->question.SuppressUnusable    = mDNSfalse;
-       zd->question.WakeOnResolve       = mDNSfalse;
+       zd->question.SearchListIndex     = 0;
+       zd->question.AppendSearchDomains = 0;
+       zd->question.RetryWithSearchDomains = mDNSfalse;
+       zd->question.TimeoutQuestion     = 0;
+       zd->question.WakeOnResolve       = 0;
+       zd->question.qnameOrig           = mDNSNULL;
        zd->question.QuestionCallback    = GetZoneData_QuestionCallback;
        zd->question.QuestionContext     = zd;
 
@@ -1604,15 +1621,46 @@ mDNSexport ZoneData *StartGetZoneData(mDNS *const m, const domainname *const nam
        zd->ZoneDataContext  = ZoneDataContext;
 
        zd->question.QuestionContext = zd;
-       AssignDomainName(&zd->question.qname, zd->CurrentSOA);
 
        mDNS_DropLockBeforeCallback();          // GetZoneData_StartQuery expects to be called from a normal callback, so we emulate that here
-       GetZoneData_StartQuery(m, zd, kDNSType_SOA);
+       if (AuthInfo && AuthInfo->AutoTunnel && !mDNSIPPortIsZero(AuthInfo->port))
+               {
+               LogInfo("StartGetZoneData: Bypassing SOA, SRV query for %##s", AuthInfo->domain.c);
+               // We bypass SOA and SRV queries if we know the hostname and port already from the configuration.
+               // Today this is only true for AutoTunnel. As we bypass, we need to infer a few things:
+               //
+               // 1. Zone name is the same as the AuthInfo domain 
+               // 2. ZoneClass is kDNSClass_IN which should be a safe assumption
+               //
+               // If we want to make this bypass mechanism work for non-AutoTunnels also, (1) has to hold
+               // good. Otherwise, it has to be configured also.
+
+               AssignDomainName(&zd->ZoneName, &AuthInfo->domain);
+               zd->ZoneClass = kDNSClass_IN;
+               AssignDomainName(&zd->Host, &AuthInfo->hostname);
+               zd->Port = AuthInfo->port;
+               AssignDomainName(&zd->question.qname, &zd->Host);
+               GetZoneData_StartQuery(m, zd, kDNSType_A);
+               }
+       else
+               {
+               if (AuthInfo && AuthInfo->AutoTunnel) LogInfo("StartGetZoneData: Not Bypassing SOA, SRV query for %##s", AuthInfo->domain.c);
+               AssignDomainName(&zd->question.qname, zd->CurrentSOA);
+               GetZoneData_StartQuery(m, zd, kDNSType_SOA);
+               }
        mDNS_ReclaimLockAfterCallback();
 
        return zd;
        }
 
+// Returns if the question is a GetZoneData question. These questions are special in
+// that they are created internally while resolving a private query or LLQs.
+mDNSexport mDNSBool IsGetZoneDataQuestion(DNSQuestion *q)
+       {
+       if (q->QuestionCallback == GetZoneData_QuestionCallback) return(mDNStrue);
+       else return(mDNSfalse);
+       }
+
 // GetZoneData queries are a special case -- even if we have a key for them, we don't do them privately,
 // because that would result in an infinite loop (i.e. to do a private query we first need to get
 // the _dns-query-tls SRV record for the zone, and we can't do *that* privately because to do so
@@ -1668,7 +1716,7 @@ mDNSlocal void UpdateAllServiceRecords(mDNS *const m, AuthRecord *rr, mDNSBool r
                                }
                        else 
                                {
-                               // Clearing SRVchanged is a safety measure. If our pewvious dereg never
+                               // Clearing SRVchanged is a safety measure. If our pevious dereg never
                                // came back and we had a target change, we are starting fresh
                                r->SRVChanged = mDNSfalse;
                                // if it is already registered or in the process of registering, then don't
@@ -2053,7 +2101,7 @@ mDNSlocal void AdvertiseHostname(mDNS *m, HostnameInfo *h)
        {
        if (!mDNSIPv4AddressIsZero(m->AdvertisedV4.ip.v4) && h->arv4.resrec.RecordType == kDNSRecordTypeUnregistered)
                {
-               mDNS_SetupResourceRecord(&h->arv4, mDNSNULL, mDNSInterface_Any, kDNSType_A, kHostNameTTL, kDNSRecordTypeUnregistered, HostnameCallback, h);
+               mDNS_SetupResourceRecord(&h->arv4, mDNSNULL, mDNSInterface_Any, kDNSType_A, kHostNameTTL, kDNSRecordTypeUnregistered, AuthRecordAny, HostnameCallback, h);
                AssignDomainName(&h->arv4.namestorage, &h->fqdn);
                h->arv4.resrec.rdata->u.ipv4 = m->AdvertisedV4.ip.v4;
                h->arv4.state = regState_Unregistered;
@@ -2079,7 +2127,7 @@ mDNSlocal void AdvertiseHostname(mDNS *m, HostnameInfo *h)
 
        if (!mDNSIPv6AddressIsZero(m->AdvertisedV6.ip.v6) && h->arv6.resrec.RecordType == kDNSRecordTypeUnregistered)
                {
-               mDNS_SetupResourceRecord(&h->arv6, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL, kDNSRecordTypeKnownUnique, HostnameCallback, h);
+               mDNS_SetupResourceRecord(&h->arv6, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, HostnameCallback, h);
                AssignDomainName(&h->arv6.namestorage, &h->fqdn);
                h->arv6.resrec.rdata->u.ipv6 = m->AdvertisedV6.ip.v6;
                h->arv6.state = regState_Unregistered;
@@ -2220,7 +2268,12 @@ mDNSlocal void GetStaticHostname(mDNS *m)
        q->ForceMCast       = mDNSfalse;
        q->ReturnIntermed   = mDNStrue;
        q->SuppressUnusable = mDNSfalse;
-       q->WakeOnResolve    = mDNSfalse;
+       q->SearchListIndex  = 0;
+       q->AppendSearchDomains = 0;
+       q->RetryWithSearchDomains = mDNSfalse;
+       q->TimeoutQuestion  = 0;
+       q->WakeOnResolve    = 0;
+       q->qnameOrig        = mDNSNULL;
        q->QuestionCallback = FoundStaticHostname;
        q->QuestionContext  = mDNSNULL;
 
@@ -2959,7 +3012,7 @@ mDNSlocal mDNSBool SendGroupUpdates(mDNS *const m)
                                }
                        spaceleft -= rrSize;
                        oldnext = next;
-                       LogInfo("SendGroupUpdates: Building a message with resource record %s, next %p, state %d", ARDisplayString(m, rr), next, rr->state);
+                       LogInfo("SendGroupUpdates: Building a message with resource record %s, next %p, state %d, ttl %d", ARDisplayString(m, rr), next, rr->state, rr->resrec.rroriginalttl);
                        if (!(next = BuildUpdateMessage(m, next, rr, limit)))
                                {
                                // We calculated the space and if we can't fit in, we had some bug in the calculation,
@@ -4062,7 +4115,6 @@ mDNSlocal const mDNSu8 *mDNS_WABLabels[] =
        (const mDNSu8 *)"\002lb",
        (const mDNSu8 *)"\001r",
        (const mDNSu8 *)"\002dr",
-       (const mDNSu8 *)"\002cf",
        (const mDNSu8 *)mDNSNULL,
        };
 
@@ -4601,28 +4653,41 @@ mDNSexport void SleepRecordRegistrations(mDNS *m)
                }
        }
 
-mDNSexport void mDNS_AddSearchDomain(const domainname *const domain)
+mDNSexport void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID)
        {
        SearchListElem **p;
+       SearchListElem *tmp = mDNSNULL;
 
        // Check to see if we already have this domain in our list
        for (p = &SearchList; *p; p = &(*p)->next)
-               if (SameDomainName(&(*p)->domain, domain))
+               if (((*p)->InterfaceID == InterfaceID) && SameDomainName(&(*p)->domain, domain))
                        {
                        // If domain is already in list, and marked for deletion, unmark the delete
                        // Be careful not to touch the other flags that may be present
-                       if ((*p)->flag & SLE_DELETE) (*p)->flag &= ~SLE_DELETE;
                        LogInfo("mDNS_AddSearchDomain already in list %##s", domain->c);
-                       return;
+                       if ((*p)->flag & SLE_DELETE) (*p)->flag &= ~SLE_DELETE;
+                       tmp = *p;
+                       *p = tmp->next;
+                       tmp->next = mDNSNULL;
+                       break;
                        }
 
-       // if domain not in list, add to list, mark as add (1)
-       *p = mDNSPlatformMemAllocate(sizeof(SearchListElem));
-       if (!*p) { LogMsg("ERROR: mDNS_AddSearchDomain - malloc"); return; }
-       mDNSPlatformMemZero(*p, sizeof(SearchListElem));
-       AssignDomainName(&(*p)->domain, domain);
-       (*p)->next = mDNSNULL;
-       LogInfo("mDNS_AddSearchDomain created new %##s", domain->c);
+       
+       // move to end of list so that we maintain the same order
+       while (*p) p = &(*p)->next;
+       
+       if (tmp) *p = tmp; 
+       else
+               {
+               // if domain not in list, add to list, mark as add (1)
+               *p = mDNSPlatformMemAllocate(sizeof(SearchListElem));
+               if (!*p) { LogMsg("ERROR: mDNS_AddSearchDomain - malloc"); return; }
+               mDNSPlatformMemZero(*p, sizeof(SearchListElem));
+               AssignDomainName(&(*p)->domain, domain);
+               (*p)->next = mDNSNULL;
+               (*p)->InterfaceID = InterfaceID;
+               LogInfo("mDNS_AddSearchDomain created new %##s, InterfaceID %p", domain->c, InterfaceID);
+               }
        }
 
 mDNSlocal void FreeARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
@@ -4631,95 +4696,6 @@ mDNSlocal void FreeARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus r
        if (result == mStatus_MemFree) mDNSPlatformMemFree(rr->RecordContext);
        }
 
-#if APPLE_OSX_mDNSResponder
-mDNSlocal void CheckAutoTunnel6Registration(mDNS *const m, mDNSBool RegisterAutoTunnel6)
-       {
-       LogInfo("CheckAutoTunnel6Registration: Current value RegisterAutoTunnel6 %d, New value %d", m->RegisterAutoTunnel6, RegisterAutoTunnel6);
-       if (!RegisterAutoTunnel6)
-               {
-               // We are not supposed to register autotunnel6. If we had previously registered
-               // autotunnel6, deregister it now.
-               if (m->RegisterAutoTunnel6)
-                       {
-                       m->RegisterAutoTunnel6 = mDNSfalse;
-                       LogInfo("CheckAutoTunnel6Registration: Removing AutoTunnel6");
-                       RemoveAutoTunnel6Record(m);
-                       }
-               else LogInfo("CheckAutoTunnel6Registration: Already Removed AutoTunnel6");
-               }
-       else 
-               {
-               // We are supposed to register autotunnel6. If we had previously  de-registered
-               // autotunnel6, re-register it now.
-               if (!m->RegisterAutoTunnel6)
-                       {
-                       m->RegisterAutoTunnel6 = mDNStrue;
-                       LogInfo("CheckAutoTunnel6Registration: Adding AutoTunnel6");
-                       SetupConndConfigChanges(m);
-                       }
-               else LogInfo("CheckAutoTunnel6Registration: already Added AutoTunnel6");
-               }
-       }
-#endif
-
-mDNSlocal void FoundCFDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
-       {
-       SearchListElem *slElem = question->QuestionContext;
-       mDNSBool RegisterAutoTunnel6 = mDNStrue;
-       char *res = "DisableInboundRelay";
-       
-       LogInfo("FoundCFDomain: InterfaceID %p %s Question %##s Answer %s", answer->InterfaceID, AddRecord ? "Add" : "Rmv", question->qname.c, RRDisplayString(m, answer));
-       if (answer->rrtype != kDNSType_TXT)
-               {
-               LogMsg("FoundCFDomain: answer type is not TXT %s for question %##s", DNSTypeName(answer->rrtype), question->qname.c);
-               return;
-               }
-       if (answer->RecordType == kDNSRecordTypePacketNegative)
-               {
-               LogInfo("FoundCFDomain: Negative answer for %##s", question->qname.c);
-               return;
-               }
-       if (answer->InterfaceID == mDNSInterface_LocalOnly)
-               {
-               LogInfo("FoundCFDomain: LocalOnly interfaceID for %##s", question->qname.c);
-               return;
-               }
-
-       // TXT record is encoded as <len><data>
-       if (answer->rdlength != mDNSPlatformStrLen(res) + 1)
-               {
-               LogInfo("FoundCFDomain: Invalid TXT record to disable %##s, length %d", question->qname.c, answer->rdlength);
-               return;
-               }
-
-       // Compare the data (excluding the len byte)
-       if (!mDNSPlatformMemSame(&answer->rdata->u.txt.c[1], res, answer->rdlength - 1))
-               {
-               LogInfo("FoundCFDomain: Invalid TXT record to disable %##s", question->qname.c);
-               return;
-               }
-
-       // It is sufficient for one answer to disable registration of autotunnel6. But we should
-       // have zero answers across all domains to register autotunnel6.
-       if (AddRecord)
-               {
-               slElem->numCfAnswers++;
-               RegisterAutoTunnel6 = mDNSfalse;
-               }
-       else
-               {
-               const SearchListElem *s;
-               slElem->numCfAnswers--;
-               if (slElem->numCfAnswers < 0) LogMsg("FoundCFDomain: numCfAnswers less than zero %d", slElem->numCfAnswers);
-               // See if any domain (including the slElem) has any answers
-               for (s=SearchList; s; s=s->next)
-                       if (s->numCfAnswers) { RegisterAutoTunnel6 = mDNSfalse; break; }
-               }
-#if APPLE_OSX_mDNSResponder
-       CheckAutoTunnel6Registration(m, RegisterAutoTunnel6);
-#endif
-       }
-
 mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
        {
        SearchListElem *slElem = question->QuestionContext;
@@ -4743,7 +4719,7 @@ mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceR
                {
                ARListElem *arElem = mDNSPlatformMemAllocate(sizeof(ARListElem));
                if (!arElem) { LogMsg("ERROR: FoundDomain out of memory"); return; }
-               mDNS_SetupResourceRecord(&arElem->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, FreeARElemCallback, arElem);
+               mDNS_SetupResourceRecord(&arElem->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, AuthRecordLocalOnly, FreeARElemCallback, arElem);
                MakeDomainNameFromDNSNameString(&arElem->ar.namestorage, name);
                AppendDNSNameString            (&arElem->ar.namestorage, "local");
                AssignDomainName(&arElem->ar.resrec.rdata->u.name, &answer->rdata->u.name);
@@ -4790,7 +4766,7 @@ mDNSexport void udns_validatelists(void *const v)
 
        DomainAuthInfo *info;
        for (info = m->AuthInfoList; info; info = info->next)
-               if (info->next == (DomainAuthInfo *)~0 || info->AutoTunnel == (mDNSBool)~0)
+               if (info->next == (DomainAuthInfo *)~0 || info->AutoTunnel == (const char*)~0)
                        LogMemCorruption("m->AuthInfoList: %p is garbage (%X)", info, info->AutoTunnel);
 
        HostnameInfo *hi;
@@ -4805,40 +4781,19 @@ mDNSexport void udns_validatelists(void *const v)
        }
 #endif
 
-mDNSlocal void mDNS_StartCFQuestion(mDNS *const m, DNSQuestion *question, domainname *domain, void *context)
-       {
-       AssignDomainName (&question->qname, (const domainname*)"\002cf" "\007_dns-sd" "\x04_udp");
-       AppendDomainName (&question->qname, domain);
-       question->InterfaceID      = mDNSInterface_Any;
-       question->Target           = zeroAddr;
-       question->qtype            = kDNSType_TXT;
-       question->qclass           = kDNSClass_IN;
-       question->LongLived        = mDNSfalse;
-       question->ExpectUnique     = mDNStrue;
-       question->ForceMCast       = mDNSfalse;
-       question->ReturnIntermed   = mDNSfalse;
-       question->SuppressUnusable = mDNSfalse;
-       question->WakeOnResolve    = mDNSfalse;
-       question->QuestionCallback = FoundCFDomain;
-       question->QuestionContext  = context;
-       LogInfo("mDNS_StartCFQuestion: Start CF domain question %##s", question->qname.c);
-       if (mDNS_StartQuery(m, question))
-               LogMsg("mDNS_StartCFQuestion: ERROR!! cannot start cf._dns-sd query");
-       }
-
 // This should probably move to the UDS daemon -- the concept of legacy clients and automatic registration / automatic browsing
 // is really a UDS API issue, not something intrinsic to uDNS
+
 mDNSexport mStatus uDNS_SetupSearchDomains(mDNS *const m, int action)
        {
        SearchListElem **p = &SearchList, *ptr;
-       const SearchListElem *s;
-       mDNSBool RegisterAutoTunnel6 = mDNStrue;
        mStatus err;
 
        // step 1: mark each element for removal
        for (ptr = SearchList; ptr; ptr = ptr->next) ptr->flag |= SLE_DELETE;
 
-       // Client has requested domain enumeration or automatic browse -- time to make sure we have the search domains from the platform layer
+       // Make sure we have the search domains from the platform layer so that if we start the WAB
+       // queries below, we have the latest information
        mDNS_Lock(m);
        mDNSPlatformSetDNSConfig(m, mDNSfalse, mDNStrue, mDNSNULL, mDNSNULL, mDNSNULL);
        mDNS_Unlock(m);
@@ -4850,7 +4805,7 @@ mDNSexport mStatus uDNS_SetupSearchDomains(mDNS *const m, int action)
        while (*p)
                {
                ptr = *p;
-               LogInfo("uDNS_SetupSearchDomains:action %d: Flags %d,  AuthRecs %p, %##s", action, ptr->flag, ptr->AuthRecs, ptr->domain.c);
+               LogInfo("uDNS_SetupSearchDomains:action %d: Flags %d,  AuthRecs %p, InterfaceID %p %##s", action, ptr->flag, ptr->AuthRecs, ptr->InterfaceID, ptr->domain.c);
                if (ptr->flag & SLE_DELETE)
                        {
                        ARListElem *arList = ptr->AuthRecs;
@@ -4858,10 +4813,10 @@ mDNSexport mStatus uDNS_SetupSearchDomains(mDNS *const m, int action)
                        *p = ptr->next;
 
                        // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries
-                       // Note: Stopping a question will not generate the RMV events for the question (handled in FoundCFDomain)
-                       // and hence we need to recheck all the domains to see if we need to register/deregister _autotunnel6.
-                       // This is done at the end.
-                       if ((ptr->flag & SLE_WAB_QUERY_STARTED) && !SameDomainName(&ptr->domain, &localdomain))
+                       // We suppressed the domain enumeration for scoped search domains below. When we enable that
+                       // enable this.
+                       if ((ptr->flag & SLE_WAB_QUERY_STARTED) && 
+                               !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any))
                                {
                                mDNS_StopGetDomains(m, &ptr->BrowseQ);
                                mDNS_StopGetDomains(m, &ptr->RegisterQ);
@@ -4869,12 +4824,7 @@ mDNSexport mStatus uDNS_SetupSearchDomains(mDNS *const m, int action)
                                mDNS_StopGetDomains(m, &ptr->DefRegisterQ);
                                mDNS_StopGetDomains(m, &ptr->AutomaticBrowseQ);
                                }
-#if !TARGET_OS_EMBEDDED
-                       if ((ptr->flag & SLE_CF_QUERY_STARTED) && !SameDomainName(&ptr->domain, &localdomain))
-                               {
-                               mDNS_StopGetDomains(m, &ptr->CfQ);
-                               }
-#endif
+
                        mDNSPlatformMemFree(ptr);
 
                // deregister records generated from answers to the query
@@ -4893,14 +4843,15 @@ mDNSexport mStatus uDNS_SetupSearchDomains(mDNS *const m, int action)
                if ((action & UDNS_START_WAB_QUERY) && !(ptr->flag & SLE_WAB_QUERY_STARTED))
                        {
                        // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries.
-                       if (!SameDomainName(&ptr->domain, &localdomain))
+                       // Also, suppress the domain enumeration for scoped search domains for now until there is a need.
+                       if (!SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any))
                                {
                                mStatus err1, err2, err3, err4, err5;
-                               err1 = mDNS_GetDomains(m, &ptr->BrowseQ,          mDNS_DomainTypeBrowse,              &ptr->domain, mDNSInterface_Any, FoundDomain, ptr);
-                               err2 = mDNS_GetDomains(m, &ptr->DefBrowseQ,       mDNS_DomainTypeBrowseDefault,       &ptr->domain, mDNSInterface_Any, FoundDomain, ptr);
-                               err3 = mDNS_GetDomains(m, &ptr->RegisterQ,        mDNS_DomainTypeRegistration,        &ptr->domain, mDNSInterface_Any, FoundDomain, ptr);
-                               err4 = mDNS_GetDomains(m, &ptr->DefRegisterQ,     mDNS_DomainTypeRegistrationDefault, &ptr->domain, mDNSInterface_Any, FoundDomain, ptr);
-                               err5 = mDNS_GetDomains(m, &ptr->AutomaticBrowseQ, mDNS_DomainTypeBrowseAutomatic,     &ptr->domain, mDNSInterface_Any, FoundDomain, ptr);
+                               err1 = mDNS_GetDomains(m, &ptr->BrowseQ,          mDNS_DomainTypeBrowse,              &ptr->domain, ptr->InterfaceID, FoundDomain, ptr);
+                               err2 = mDNS_GetDomains(m, &ptr->DefBrowseQ,       mDNS_DomainTypeBrowseDefault,       &ptr->domain, ptr->InterfaceID, FoundDomain, ptr);
+                               err3 = mDNS_GetDomains(m, &ptr->RegisterQ,        mDNS_DomainTypeRegistration,        &ptr->domain, ptr->InterfaceID, FoundDomain, ptr);
+                               err4 = mDNS_GetDomains(m, &ptr->DefRegisterQ,     mDNS_DomainTypeRegistrationDefault, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr);
+                               err5 = mDNS_GetDomains(m, &ptr->AutomaticBrowseQ, mDNS_DomainTypeBrowseAutomatic,     &ptr->domain, ptr->InterfaceID, FoundDomain, ptr);
                                if (err1 || err2 || err3 || err4 || err5)
                                        LogMsg("uDNS_SetupSearchDomains: GetDomains for domain %##s returned error(s):\n"
                                                   "%d (mDNS_DomainTypeBrowse)\n"
@@ -4912,28 +4863,90 @@ mDNSexport mStatus uDNS_SetupSearchDomains(mDNS *const m, int action)
                                ptr->flag |= SLE_WAB_QUERY_STARTED;
                                }
                        }
-#if !TARGET_OS_EMBEDDED
-               if ((action & UDNS_START_CF_QUERY) && !(ptr->flag & SLE_CF_QUERY_STARTED))
+
+               p = &ptr->next;
+               }
+       return mStatus_NoError;
+       }
+
+mDNSexport domainname  *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal)
+       {
+       SearchListElem *p = SearchList;
+       int count = *searchIndex;
+       (void) m; // unused
+
+       if (count < 0) { LogMsg("uDNS_GetNextSearchDomain: count %d less than zero", count); return mDNSNULL; }
+
+       // skip the  domains that we already looked at before
+       for (; count; count--) p = p->next;
+
+       while (p)
+               {
+               int labels = CountLabels(&p->domain);
+               if (labels > 0)
                        {
-                       if (!SameDomainName(&ptr->domain, &localdomain))
+                       const domainname *d = SkipLeadingLabels(&p->domain, labels - 1);
+                       if (SameDomainLabel(d->c, (const mDNSu8 *)"\x4""arpa"))
+                               {
+                               LogInfo("uDNS_GetNextSearchDomain: skipping search domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID);
+                               (*searchIndex)++;
+                               p = p->next;
+                               continue;
+                               }
+                       if (ignoreDotLocal && SameDomainLabel(d->c, (const mDNSu8 *)"\x5""local"))
                                {
-                               mDNS_StartCFQuestion(m, &ptr->CfQ, &ptr->domain, ptr);
-                               ptr->flag |= SLE_CF_QUERY_STARTED;
+                               LogInfo("uDNS_GetNextSearchDomain: skipping local domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID);
+                               (*searchIndex)++;
+                               p = p->next;
+                               continue;
                                }
                        }
-#endif
+               // Point to the next one in the list which we will look at next time.
+               (*searchIndex)++;
+               // When we are appending search domains in a ActiveDirectory domain, the question's InterfaceID
+               // set to mDNSInterface_Unicast. Match the unscoped entries in that case.
+               if (((InterfaceID == mDNSInterface_Unicast) && (p->InterfaceID == mDNSInterface_Any)) ||
+                       p->InterfaceID == InterfaceID)
+                       {
+                       LogInfo("uDNS_GetNextSearchDomain returning domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID);
+                       return &p->domain;
+                       }
+               LogInfo("uDNS_GetNextSearchDomain skipping domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID);
+               p = p->next;
+               }
+       return mDNSNULL;
+       }
 
-               p = &ptr->next;
+mDNSlocal void FlushAddressCacheRecords(mDNS *const m)
+       {
+       mDNSu32 slot;
+       CacheGroup *cg;
+       CacheRecord *cr;
+       FORALL_CACHERECORDS(slot, cg, cr)
+               {
+               if (cr->resrec.InterfaceID) continue;
+
+               // If a resource record can answer A or AAAA, they need to be flushed so that we will
+               // never used to deliver an ADD or RMV
+               if (RRTypeAnswersQuestionType(&cr->resrec, kDNSType_A) ||
+                       RRTypeAnswersQuestionType(&cr->resrec, kDNSType_AAAA))
+                       {
+                       LogInfo("FlushAddressCacheRecords: Purging Resourcerecord %s", CRDisplayString(m, cr));
+                       mDNS_PurgeCacheResourceRecord(m, cr);
+                       }
                }
-#if !TARGET_OS_EMBEDDED
-       // if there is any domain has answers, need to deregister autotunnel6
-       for (s=SearchList; s; s=s->next)
-               if (s->numCfAnswers) { RegisterAutoTunnel6 = mDNSfalse; break; }
-#if APPLE_OSX_mDNSResponder
-       CheckAutoTunnel6Registration(m, RegisterAutoTunnel6);
-#endif
-#endif
-       return mStatus_NoError;
+       }
+
+// Retry questions which has seach domains appended
+mDNSexport void RetrySearchDomainQuestions(mDNS *const m)
+       {
+       // Purge all the A/AAAA cache records and restart the queries. mDNSCoreRestartAddressQueries
+       // does this. When we restart the question,  we first want to try the new search domains rather
+       // than use the entries that is already in the cache. When we appended search domains, we might
+       // have created cache entries which is no longer valid as there are new search domains now
+
+       LogInfo("RetrySearchDomainQuestions: Calling mDNSCoreRestartAddressQueries");
+       mDNSCoreRestartAddressQueries(m, mDNStrue, FlushAddressCacheRecords, mDNSNULL, mDNSNULL);
        }
 
 // Construction of Default Browse domain list (i.e. when clients pass NULL) is as follows:
@@ -4952,5 +4965,5 @@ struct CompileTimeAssertionChecks_uDNS
        // other overly-large structures instead of having a pointer to them, can inadvertently
        // cause structure sizes (and therefore memory usage) to balloon unreasonably.
        char sizecheck_tcpInfo_t     [(sizeof(tcpInfo_t)      <=  9056) ? 1 : -1];
-       char sizecheck_SearchListElem[(sizeof(SearchListElem) <=  4860) ? 1 : -1];
+       char sizecheck_SearchListElem[(sizeof(SearchListElem) <=  5000) ? 1 : -1];
        };
index a06b9aebe328592641180f08133df62e6b6022cd..2dfaf51ec8c99208b0db823a97a8b0c3fd562d33 100755 (executable)
 // 5 minutes
 #define MAX_UPDATE_REFRESH_COUNT       5
 #define MIN_UPDATE_REFRESH_TIME                (5 * 60 * mDNSPlatformOneSecond)
+
+// For questions that use kDNSServiceFlagsTimeout and we don't have a matching resolver e.g., no dns servers,
+// then use the default value of 30 seconds
+#define DEFAULT_UDNS_TIMEOUT   30 // in seconds
+
 // Entry points into unicast-specific routines
 
 extern void LLQGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo);
@@ -96,12 +101,11 @@ extern mStatus         uDNS_SetupDNSConfig(mDNS *const m);
 // more values for "action" which does the following:
 //
 // -UDNS_START_WAB_QUERY - start Wide Area Bonjour (domain enumeration) queries
-// -UDNS_START_CF_QUERY - start Configuration query
 
 #define UDNS_START_WAB_QUERY    0x00000001
-#define UDNS_START_CF_QUERY     0x00000002
 
 extern mStatus         uDNS_SetupSearchDomains(mDNS *const m, int action);
+extern domainname      *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal);
 
 typedef enum
        {
index 18b3899b36a256544b58ca08ab7ab6fa393689a1..e7e76d5a0cde5532db6950b71876f34ed49f0da3 100644 (file)
@@ -78,7 +78,7 @@ mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset,
                txtbuffer, (mDNSu16)(1+txtbuffer[0]),           // TXT data, length
                mDNSNULL, 0,                                                            // Subtypes (none)
                mDNSInterface_Any,                                                      // Interface ID
-               Callback, mDNSNULL);                                            // Callback and context
+               Callback, mDNSNULL, 0);                                         // Callback, context, flags 
 
        ConvertDomainNameToCString(recordset->RR_SRV.resrec.name, buffer);
        printf("Made Service Records for %s\n", buffer);
diff --git a/mDNSMacOSX/BonjourEvents-Info.plist b/mDNSMacOSX/BonjourEvents-Info.plist
new file mode 100644 (file)
index 0000000..6748eaa
--- /dev/null
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>English</string>
+       <key>CFBundleExecutable</key>
+       <string>${EXECUTABLE_NAME}</string>
+       <key>CFBundleName</key>
+       <string>${PRODUCT_NAME}</string>
+       <key>CFBundleIconFile</key>
+       <string></string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.${PRODUCT_NAME:rfc1034Identifier}</string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundlePackageType</key>
+       <string>BNDL</string>
+       <key>CFBundleSignature</key>
+       <string>????</string>
+       <key>CFBundleVersion</key>
+       <string>1</string>
+       <key>CFBundleShortVersionString</key>
+       <string>1.0</string>
+       <key>CFPlugInDynamicRegisterFunction</key>
+       <string></string>
+       <key>CFPlugInDynamicRegistration</key>
+       <string>NO</string>
+       <key>CFPlugInFactories</key>
+       <dict>
+               <key>FB86416D-6164-2070-726F-70735C216EC0</key>
+               <string>UserEventAgentFactory</string>
+       </dict>
+       <key>CFPlugInTypes</key>
+       <dict>
+               <key>FC86416D-6164-2070-726F-70735C216EC0</key>
+               <array>
+                       <string>FB86416D-6164-2070-726F-70735C216EC0</string>
+               </array>
+       </dict>
+       <key>CFPlugInUnloadFunction</key>
+       <string></string>
+       <key>LimitLoadToSessionType</key>
+       <array>
+               <string>System</string>
+               <string>Aqua</string>
+       </array>
+</dict>
+</plist>
diff --git a/mDNSMacOSX/BonjourEvents.c b/mDNSMacOSX/BonjourEvents.c
new file mode 100644 (file)
index 0000000..6c5d700
--- /dev/null
@@ -0,0 +1,1135 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <dns_sd.h>
+#include <UserEventAgentInterface.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+
+#pragma mark -
+#pragma mark Types
+#pragma mark -
+static const char*                     sPluginIdentifier               = "com.apple.bonjour.events";
+
+// PLIST Keys
+static const CFStringRef       sServiceNameKey                 = CFSTR("ServiceName");
+static const CFStringRef       sServiceTypeKey                 = CFSTR("ServiceType");
+static const CFStringRef       sServiceDomainKey               = CFSTR("ServiceDomain");
+
+static const CFStringRef       sOnServiceAddKey                = CFSTR("OnServiceAdd");
+static const CFStringRef       sOnServiceRemoveKey             = CFSTR("OnServiceRemove");
+static const CFStringRef    sWhileServiceExistsKey     = CFSTR("WhileServiceExists");
+
+static const CFStringRef       sLaunchdTokenKey                = CFSTR("LaunchdToken");
+
+static const CFStringRef       sPluginTimersKey                = CFSTR("PluginTimers");
+
+
+/************************************************
+ * Launch Event Dictionary (input from launchd)
+ * Passed To: ManageEventsCallback
+ *-----------------------------------------------
+ * Typing in this dictionary is not enforced 
+ * above us. So this may not be true. Type check
+ * all input before using it.
+ *-----------------------------------------------
+ * sServiceNameKey             - CFString (Optional) 
+ * sServiceTypeKey             - CFString
+ * sServiceDomainKey   - CFString
+ *
+ * One or more of the following.
+ *-----------------------------------
+ * sOnServiceAddKey                    - CFBoolean
+ * sOnServiceRemoveKey         - CFBoolean 
+ * sWhileServiceExistsKey      - CFBoolean
+ ************************************************/
+
+/************************************************
+ * Browser Dictionary
+ *-----------------------------------------------
+ * sServiceDomainKey - CFString
+ * sServiceTypeKey   - CFString
+ ************************************************/
+
+/************************************************
+ * Event Dictionary
+ *-----------------------------------------------
+ * sServiceNameKey      - CFString (Optional) 
+ * sLaunchdTokenKey     - CFNumber
+ ************************************************/
+
+typedef struct {
+    UserEventAgentInterfaceStruct*             _UserEventAgentInterface;
+    CFUUIDRef                                                  _factoryID;
+    UInt32                                                             _refCount;
+       
+       void*                                                           _pluginContext;
+       
+       CFMutableDictionaryRef                          _tokenToBrowserMap;             // Maps a token to a browser that can be used to scan the remaining dictionaries.
+       CFMutableDictionaryRef                          _browsers;                              // A Dictionary of "Browser Dictionarys" where the resposible browser is the key.
+       CFMutableDictionaryRef                          _onAddEvents;                   // A Dictionary of "Event Dictionarys" that describe events to trigger on a service appearing.
+       CFMutableDictionaryRef                          _onRemoveEvents;                // A Dictionary of "Event Dictionarys" that describe events to trigger on a service disappearing.
+       CFMutableDictionaryRef                          _whileServiceExist;             // A Dictionary of "Event Dictionarys" that describe events to trigger on a service disappearing.
+
+       
+       CFMutableArrayRef                                       _timers;
+
+} BonjourUserEventsPlugin;
+
+
+typedef struct {
+       
+       CFIndex  refCount;
+       BonjourUserEventsPlugin* plugin;
+       CFNumberRef token;
+       
+} TimerContextInfo;
+
+typedef struct {
+       CFIndex refCount;
+       DNSServiceRef browserRef;
+} NetBrowserInfo;
+
+#pragma mark -
+#pragma mark Prototypes
+#pragma mark -
+// COM Stuff
+static HRESULT QueryInterface(void *myInstance, REFIID iid, LPVOID *ppv);
+static ULONG   AddRef(void* instance);
+static ULONG   Release(void* instance);
+
+static BonjourUserEventsPlugin* Alloc(CFUUIDRef factoryID);
+static void Dealloc(BonjourUserEventsPlugin* plugin);
+
+void * UserEventAgentFactory(CFAllocatorRef allocator, CFUUIDRef typeID);
+
+// Plugin Management
+static void Install(void* instance);
+static void ManageEventsCallback(
+                                                 UserEventAgentLaunchdAction action,
+                                                 CFNumberRef                 token,
+                                                 CFTypeRef                   eventMatchDict,
+                                                 void                      * vContext);
+
+
+// Plugin Guts
+void AddEventToPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchdToken, CFDictionaryRef eventParameters);
+void RemoveEventFromPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef        launchToken);
+
+NetBrowserInfo* CreateBrowserForTypeAndDomain(BonjourUserEventsPlugin* plugin, CFStringRef type, CFStringRef domain);
+NetBrowserInfo* BrowserForSDRef(BonjourUserEventsPlugin* plugin, DNSServiceRef sdRef);
+void AddEventDictionary(CFDictionaryRef eventDict, CFMutableDictionaryRef allEventsDictionary, NetBrowserInfo* key);
+void RemoveEventFromArray(CFMutableArrayRef array, CFNumberRef launchdToken);
+
+// Net Service Browser Stuff
+void ServiceBrowserCallback (DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char* serviceName, const char* regtype, const char* replyDomain, void* context);
+void HandleTemporaryEventsForService(BonjourUserEventsPlugin* plugin, NetBrowserInfo* browser, CFStringRef serviceName, CFMutableDictionaryRef eventsDictionary);
+void HandleStateEventsForService(BonjourUserEventsPlugin* plugin, NetBrowserInfo* browser,  CFStringRef serviceName, Boolean didAppear);
+void TemporaryEventTimerCallout ( CFRunLoopTimerRef timer, void *info );
+
+// Convence Stuff
+const char* CStringFromCFString(CFStringRef string);
+
+
+// TimerContextInfo "Object"
+TimerContextInfo* TimerContextInfoCreate(BonjourUserEventsPlugin* plugin, CFNumberRef token);
+const void* TimerContextInfoRetain(const void* info);
+void TimerContextInfoRelease(const void* info);
+CFStringRef TimerContextInfoCopyDescription(const void* info);
+
+// NetBrowserInfo "Object"
+NetBrowserInfo* NetBrowserInfoCreate(CFStringRef serviceType, CFStringRef domain, void* context);
+const void* NetBrowserInfoRetain(CFAllocatorRef allocator, const void* info);
+void NetBrowserInfoRelease(CFAllocatorRef allocator, const void* info);
+Boolean        NetBrowserInfoEqual(const void *value1, const void *value2);
+CFHashCode     NetBrowserInfoHash(const void *value);
+CFStringRef    NetBrowserInfoCopyDescription(const void *value);
+
+
+static const CFDictionaryKeyCallBacks kNetBrowserInfoDictionaryKeyCallbacks = { 
+       0, 
+       NetBrowserInfoRetain, 
+       NetBrowserInfoRelease,
+       NetBrowserInfoCopyDescription,
+       NetBrowserInfoEqual,
+       NetBrowserInfoHash
+};
+
+static const CFDictionaryValueCallBacks kNetBrowserInfoDictionaryValueCallbacks = { 
+       0, 
+       NetBrowserInfoRetain, 
+       NetBrowserInfoRelease,
+       NetBrowserInfoCopyDescription,
+       NetBrowserInfoEqual     
+};
+
+// COM type definition goop.
+static UserEventAgentInterfaceStruct UserEventAgentInterfaceFtbl = {
+       NULL,                   // Required padding for COM
+       QueryInterface,                 // Query Interface
+       AddRef,                                 // AddRef()
+       Release,                                // Release()
+       Install                                 // Install 
+}; 
+
+#pragma mark -
+#pragma mark COM Management
+#pragma mark -
+
+/*****************************************************************************
+ *****************************************************************************/
+static HRESULT QueryInterface(void *myInstance, REFIID iid, LPVOID *ppv) 
+{
+       CFUUIDRef interfaceID = CFUUIDCreateFromUUIDBytes(NULL, iid);
+       
+       // Test the requested ID against the valid interfaces.
+       if(CFEqual(interfaceID, kUserEventAgentInterfaceID)) 
+       {
+               ((BonjourUserEventsPlugin *) myInstance)->_UserEventAgentInterface->AddRef(myInstance);
+               *ppv = myInstance;
+               CFRelease(interfaceID);
+               return S_OK;
+       } 
+       else if(CFEqual(interfaceID, IUnknownUUID)) 
+       {
+               ((BonjourUserEventsPlugin *) myInstance)->_UserEventAgentInterface->AddRef(myInstance);
+               *ppv = myInstance;
+               CFRelease(interfaceID);
+               return S_OK;
+    } 
+       else //  Requested interface unknown, bail with error.
+       { 
+               *ppv = NULL;
+               CFRelease(interfaceID);
+               return E_NOINTERFACE;
+       }
+}
+
+/*****************************************************************************
+ *****************************************************************************/
+static ULONG AddRef(void* instance)
+{
+       BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)instance;
+       return  ++plugin->_refCount;
+}
+
+/*****************************************************************************
+ *****************************************************************************/
+static ULONG Release(void* instance)
+{
+       BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)instance;
+
+       if (plugin->_refCount != 0)
+               --plugin->_refCount;
+       
+       if (plugin->_refCount == 0)
+       {
+               Dealloc(instance);
+               return 0;
+       }
+       
+       return plugin->_refCount;
+} 
+
+/*****************************************************************************
+ * Alloc
+ * - 
+ * Functionas as both +[alloc] and -[init] for the plugin. Add any 
+ * initalization of member variables here.
+ *****************************************************************************/
+static BonjourUserEventsPlugin* Alloc(CFUUIDRef factoryID)
+{
+       BonjourUserEventsPlugin* plugin = malloc(sizeof(BonjourUserEventsPlugin));
+       
+       plugin->_UserEventAgentInterface = &UserEventAgentInterfaceFtbl;
+       plugin->_pluginContext = NULL;
+               
+       if (factoryID) 
+       {
+               plugin->_factoryID = (CFUUIDRef)CFRetain(factoryID);
+               CFPlugInAddInstanceForFactory(factoryID);
+       }
+       
+       plugin->_refCount = 1;
+       plugin->_tokenToBrowserMap = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kNetBrowserInfoDictionaryValueCallbacks);
+       plugin->_browsers = CFDictionaryCreateMutable(NULL, 0, &kNetBrowserInfoDictionaryKeyCallbacks, &kCFTypeDictionaryValueCallBacks);
+       plugin->_onAddEvents = CFDictionaryCreateMutable(NULL, 0, &kNetBrowserInfoDictionaryKeyCallbacks, &kCFTypeDictionaryValueCallBacks);
+       plugin->_onRemoveEvents = CFDictionaryCreateMutable(NULL, 0, &kNetBrowserInfoDictionaryKeyCallbacks, &kCFTypeDictionaryValueCallBacks);
+       plugin->_whileServiceExist = CFDictionaryCreateMutable(NULL, 0, &kNetBrowserInfoDictionaryKeyCallbacks, &kCFTypeDictionaryValueCallBacks);
+       
+       plugin->_timers = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+       
+       return plugin;
+}
+
+/*****************************************************************************
+ * Dealloc
+ * - 
+ * Much like Obj-C dealloc this method is responsible for releasing any object
+ * this plugin is holding. Unlike ObjC, you call directly free() instead of 
+ * [super dalloc].
+ *****************************************************************************/
+static void Dealloc(BonjourUserEventsPlugin* plugin)
+{
+       CFUUIDRef factoryID = plugin->_factoryID;
+       
+       if (factoryID)
+       {
+               CFPlugInRemoveInstanceForFactory(factoryID);
+               CFRelease(factoryID);
+       }
+       
+       if (plugin->_tokenToBrowserMap)
+               CFRelease(plugin->_tokenToBrowserMap);
+       
+       if (plugin->_browsers)
+               CFRelease(plugin->_browsers);
+       
+       if (plugin->_onAddEvents)
+               CFRelease(plugin->_onAddEvents);
+       
+       if (plugin->_onRemoveEvents)
+               CFRelease(plugin->_onRemoveEvents);
+       
+       if (plugin->_whileServiceExist)
+               CFRelease(plugin->_whileServiceExist);
+       
+       if (plugin->_timers)
+       {
+               CFIndex i;
+               CFIndex count = CFArrayGetCount(plugin->_timers);
+               CFRunLoopRef crl = CFRunLoopGetCurrent();
+               
+               for (i = 0; i < count; ++i)
+               {
+                       CFRunLoopTimerRef timer = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(plugin->_timers, i);
+                       CFRunLoopRemoveTimer(crl, timer, kCFRunLoopCommonModes);
+               }
+               
+               CFRelease(plugin->_timers);
+       }
+       
+       free(plugin);
+}
+
+/*******************************************************************************
+ *******************************************************************************/
+void * UserEventAgentFactory(CFAllocatorRef allocator, CFUUIDRef typeID)
+{
+       (void)allocator;
+    BonjourUserEventsPlugin * result = NULL;
+       
+    if (typeID && CFEqual(typeID, kUserEventAgentTypeID)) {
+        result = Alloc(kUserEventAgentFactoryID);
+    }
+       
+    return (void *)result;
+}
+
+#pragma mark -
+#pragma mark Plugin Management
+#pragma mark -
+/*****************************************************************************
+ * Install
+ * -
+ * This is invoked once when the plugin is loaded to do initial setup and
+ * allow us to register with launchd. If UserEventAgent crashes, the plugin 
+ * will need to be reloaded, and hence this will get invoked again.
+ *****************************************************************************/
+static void Install(void *instance)
+{
+       BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)instance;
+       
+       plugin->_pluginContext = UserEventAgentRegisterForLaunchEvents(sPluginIdentifier, &ManageEventsCallback, plugin);
+       
+       if (!plugin->_pluginContext)
+       {
+               fprintf(stderr, "%s: failed to register for launch events.\n", sPluginIdentifier);
+               return;
+       }
+       
+}
+
+/*****************************************************************************
+ * ManageEventsCallback
+ * - 
+ * This is invoked when launchd loads a event dictionary and needs to inform 
+ * us what a daemon / agent is looking for.
+ *****************************************************************************/
+static void ManageEventsCallback(UserEventAgentLaunchdAction action, CFNumberRef token, CFTypeRef eventMatchDict, void* vContext)
+{
+       
+       if (!eventMatchDict || CFGetTypeID(eventMatchDict) != CFDictionaryGetTypeID())
+       {
+               fprintf(stderr, "%s given non-dictionary for event dictionary\n", sPluginIdentifier);
+               return;
+       }
+       
+       if (action == kUserEventAgentLaunchdAdd)
+       {
+               // Launchd wants us to add a launch event for this token and matching dictionary.
+               AddEventToPlugin((BonjourUserEventsPlugin*)vContext, token, (CFDictionaryRef)eventMatchDict);
+       }
+       else if (action == kUserEventAgentLaunchdRemove)
+       {
+               // Launchd wants us to remove the event hook we setup for this token / matching dictionary.
+               RemoveEventFromPlugin((BonjourUserEventsPlugin*)vContext, token);
+       }
+       else
+       {
+               fprintf(stderr, "%s got unknown UserEventAction: %d\n", sPluginIdentifier, action);
+       }
+}
+
+
+#pragma mark -
+#pragma mark Plugin Guts
+#pragma mark -
+
+/*****************************************************************************
+ * AddEventToPlugin
+ * - 
+ * This method is invoked when launchd wishes the plugin to setup a launch 
+ * event matching the parameters in the dictionary.
+ *****************************************************************************/
+void AddEventToPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchdToken, CFDictionaryRef eventParameters)
+{
+       CFStringRef             domain = CFDictionaryGetValue(eventParameters, sServiceDomainKey);
+       CFStringRef             type = CFDictionaryGetValue(eventParameters, sServiceTypeKey);
+       CFStringRef             name = CFDictionaryGetValue(eventParameters, sServiceNameKey);
+       CFBooleanRef    cfOnAdd = CFDictionaryGetValue(eventParameters, sOnServiceAddKey);
+       CFBooleanRef    cfOnRemove = CFDictionaryGetValue(eventParameters, sOnServiceRemoveKey);
+       CFBooleanRef    cfWhileSericeExists = CFDictionaryGetValue(eventParameters, sWhileServiceExistsKey);
+       
+       Boolean                 onAdd = false;
+       Boolean                 onRemove = false;
+       Boolean                 whileExists = false;
+       
+       if (cfOnAdd && CFGetTypeID(cfOnRemove) == CFBooleanGetTypeID() && CFBooleanGetValue(cfOnAdd))
+               onAdd = true;
+       
+       if (cfOnRemove && CFGetTypeID(cfOnRemove) == CFBooleanGetTypeID() && CFBooleanGetValue(cfOnRemove))
+               onRemove = true;
+       
+       if (cfWhileSericeExists && CFGetTypeID(cfWhileSericeExists) == CFBooleanGetTypeID() && CFBooleanGetValue(cfWhileSericeExists))
+               whileExists = true;
+       
+       // A type is required. If none is specified, BAIL
+       if (!type || CFGetTypeID(type) != CFStringGetTypeID()) 
+       {
+               fprintf(stderr, "%s, a LaunchEvent is missing a service type.\n", sPluginIdentifier);
+               return;
+       }
+       
+       // If we aren't suppose to launch on services appearing or disappearing, this service does nothing. Ignore.
+       if ((!onAdd && !onRemove && !whileExists) || (onAdd && onRemove && whileExists))
+       {
+               fprintf(stderr, "%s, a LaunchEvent is missing both onAdd/onRemove/existance or has both.\n", sPluginIdentifier);
+               return;
+       }
+
+       // If no domain is specified, assume local.
+       if (!domain)
+       {
+               domain = CFSTR("local"); 
+       }
+       else if (CFGetTypeID(domain) != CFStringGetTypeID() ) // If the domain is not a string, fai;
+       {
+               fprintf(stderr, "%s, a LaunchEvent has a domain that is not a string.\n", sPluginIdentifier);
+               return;
+       }
+
+       
+       // If we have a name filter, but it's not a string. This event it broken, bail.
+       if (name && CFGetTypeID(name) != CFStringGetTypeID())
+       {
+               fprintf(stderr, "%s, a LaunchEvent has a domain that is not a string.\n", sPluginIdentifier);
+               return;
+       }
+       
+       // Get us a browser
+       NetBrowserInfo* browser = CreateBrowserForTypeAndDomain(plugin, type, domain);
+       
+       if (!browser)
+       {
+               fprintf(stderr, "%s, a LaunchEvent has a domain that is not a string.\n", sPluginIdentifier);
+               return;
+       }
+       
+       // Create Event Dictionary
+       CFMutableDictionaryRef eventDictionary = CFDictionaryCreateMutable(NULL, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+       
+       
+       CFDictionarySetValue(eventDictionary, sLaunchdTokenKey, launchdToken);
+       
+       if (name)
+               CFDictionarySetValue(eventDictionary, sServiceNameKey, name);
+       
+       // Add to the correct dictionary.
+       if (onAdd)
+               AddEventDictionary(eventDictionary, plugin->_onAddEvents, browser);
+       
+       if (onRemove)
+               AddEventDictionary(eventDictionary, plugin->_onRemoveEvents, browser);
+       
+       if (whileExists)
+               AddEventDictionary(eventDictionary, plugin->_whileServiceExist, browser);
+       
+       // Add Token Mapping
+       CFDictionarySetValue(plugin->_tokenToBrowserMap, launchdToken, browser);
+       
+       // Release Memory
+       CFRelease(eventDictionary);
+       NetBrowserInfoRelease(NULL, browser);
+       
+}
+
+
+
+/*****************************************************************************
+ * RemoveEventFromPlugin
+ * - 
+ * This method is invoked when launchd wishes the plugin to setup a launch 
+ * event matching the parameters in the dictionary.
+ *****************************************************************************/
+void RemoveEventFromPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef        launchdToken)
+{
+       NetBrowserInfo* browser = (NetBrowserInfo*)CFDictionaryGetValue(plugin->_tokenToBrowserMap, launchdToken);
+       Boolean othersUsingBrowser = false;
+       
+       if (!browser)
+       {
+               long long value = 0;
+               CFNumberGetValue(launchdToken, kCFNumberLongLongType, &value);
+               fprintf(stderr, "%s, Launchd asked us to remove a token we did not register!\nToken:%lld\n", sPluginIdentifier, value);
+               return;
+       }
+       
+       CFMutableArrayRef onAddEvents = (CFMutableArrayRef)CFDictionaryGetValue(plugin->_onAddEvents, browser);
+       CFMutableArrayRef onRemoveEvents = (CFMutableArrayRef)CFDictionaryGetValue(plugin->_onRemoveEvents, browser);
+       
+       if (onAddEvents)
+       {
+               RemoveEventFromArray(onAddEvents, launchdToken);
+                               
+               // Is the array now empty, clean up 
+               if (CFArrayGetCount(onAddEvents) == 0)
+                       CFDictionaryRemoveValue(plugin->_onAddEvents, browser);
+       }
+
+       if (onRemoveEvents)
+       {
+               RemoveEventFromArray(onRemoveEvents, launchdToken);
+               
+               // Is the array now empty, clean up 
+               if (CFArrayGetCount(onRemoveEvents) == 0)
+                       CFDictionaryRemoveValue(plugin->_onRemoveEvents, browser);
+       }
+       
+       // Remove ourselves from the token dictionary.
+       CFDictionaryRemoveValue(plugin->_tokenToBrowserMap, launchdToken);
+       
+       // Check to see if anyone else is using this browser.
+       CFIndex i;
+       CFIndex count = CFDictionaryGetCount(plugin->_tokenToBrowserMap);
+       NetBrowserInfo** browsers = malloc(count * sizeof(NetBrowserInfo*));
+
+       // Fetch the values of the token dictionary
+       CFDictionaryGetKeysAndValues(plugin->_tokenToBrowserMap, NULL, (const void**)browsers);
+
+       for (i = 0; i < count; ++i)
+       {
+               if (NetBrowserInfoEqual(browsers[i], browser))
+               {
+                       othersUsingBrowser = true;
+                       break;
+               }
+       }
+       
+       // If no one else is useing our browser, clean up!
+       if (!othersUsingBrowser)
+       {
+               CFDictionaryRemoveValue(plugin->_tokenToBrowserMap, launchdToken); // This triggers release and dealloc of the browser
+       }
+       
+       free(browsers);
+}
+
+
+/*****************************************************************************
+ * CreateBrowserForTypeAndDomain
+ * - 
+ * This method returns a NetBrowserInfo that is looking for a type of 
+ * service in a domain. If no browser exists, it will create one and return it.
+ *****************************************************************************/
+NetBrowserInfo* CreateBrowserForTypeAndDomain(BonjourUserEventsPlugin* plugin, CFStringRef type, CFStringRef domain)
+{
+       CFIndex i;
+       CFIndex count = CFDictionaryGetCount(plugin->_browsers);
+       NetBrowserInfo* browser = NULL;
+       CFDictionaryRef* dicts = malloc(count * sizeof(CFDictionaryRef));
+       NetBrowserInfo** browsers = malloc(count * sizeof(NetBrowserInfo*));
+       
+       // Fetch the values of the browser dictionary
+       CFDictionaryGetKeysAndValues(plugin->_browsers, (const void**)browsers, (const void**)dicts);
+       
+       // Loop thru the browsers list and see if we can find a matching one.
+       for (i = 0; i < count; ++i)
+       {
+               CFDictionaryRef browserDict = dicts[i];
+
+               CFStringRef browserType = CFDictionaryGetValue(browserDict, sServiceTypeKey);
+               CFStringRef browserDomain = CFDictionaryGetValue(browserDict, sServiceDomainKey);
+               
+               // If we have a matching browser, break
+               if (CFStringCompare(browserType, type, kCFCompareCaseInsensitive) &&
+                       CFStringCompare(browserDomain, domain, kCFCompareCaseInsensitive))
+               {
+                       browser = browsers[i];
+                       NetBrowserInfoRetain(NULL, browser);
+                       break;
+               }
+       }
+       
+       // No match found, lets create one!
+       if (!browser)
+       {
+               
+               browser = NetBrowserInfoCreate(type, domain, plugin);
+               
+               if (!browser)
+               {                       
+                       fprintf(stderr, "%s, failed to search for %s.%s", sPluginIdentifier, CStringFromCFString(type) , CStringFromCFString(domain));
+                       free(dicts);
+                       free(browsers);
+                       return NULL;
+               }
+               
+               // Service browser created, lets add this to ourselves to the dictionary.
+               CFMutableDictionaryRef browserDict = CFDictionaryCreateMutable(NULL, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+               
+               CFDictionarySetValue(browserDict, sServiceTypeKey, type);
+               CFDictionarySetValue(browserDict, sServiceDomainKey, domain);
+               
+               // Add the dictionary to the browsers dictionary.
+               CFDictionarySetValue(plugin->_browsers, browser, browserDict);
+               
+               // Release Memory
+               CFRelease(browserDict);
+       }
+       
+       free(dicts);
+       free(browsers);
+       
+       return browser;
+}
+
+/*****************************************************************************
+ * BrowserForSDRef
+ * - 
+ * This method returns a NetBrowserInfo that matches the calling SDRef passed
+ * in via the callback.
+ *****************************************************************************/
+NetBrowserInfo* BrowserForSDRef(BonjourUserEventsPlugin* plugin, DNSServiceRef sdRef)
+{
+       CFIndex i;
+       CFIndex count = CFDictionaryGetCount(plugin->_browsers);
+       NetBrowserInfo* browser = NULL;
+       NetBrowserInfo** browsers = malloc(count * sizeof(NetBrowserInfo*));
+       
+       // Fetch the values of the browser dictionary
+       CFDictionaryGetKeysAndValues(plugin->_browsers, (const void**)browsers, NULL);
+       
+       // Loop thru the browsers list and see if we can find a matching one.
+       for (i = 0; i < count; ++i)
+       {
+               NetBrowserInfo* currentBrowser = browsers[i];
+               
+               if (currentBrowser->browserRef == sdRef)
+               {
+                       browser = currentBrowser;
+                       break;
+               }
+       }
+       
+       
+       free(browsers);
+
+       return browser;
+}
+
+/*****************************************************************************
+ * AddEventDictionary
+ * - 
+ * Adds a event to a browser's event dictionary
+ *****************************************************************************/
+
+void AddEventDictionary(CFDictionaryRef eventDict, CFMutableDictionaryRef allEventsDictionary, NetBrowserInfo* key)
+{
+       CFMutableArrayRef eventsForBrowser = (CFMutableArrayRef)CFDictionaryGetValue(allEventsDictionary, key);
+       
+       if (!eventsForBrowser) // We have no events for this browser yet, lets add him.
+       {
+               eventsForBrowser = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+               CFDictionarySetValue(allEventsDictionary, key, eventsForBrowser); 
+       }
+       else 
+       {
+               CFRetain(eventsForBrowser);
+       }
+
+       CFArrayAppendValue(eventsForBrowser, eventDict);
+       CFRelease(eventsForBrowser);
+}
+
+/*****************************************************************************
+ * RemoveEventFromArray
+ * - 
+ * Searches a Array of Event Dictionaries to find one with a matching launchd
+ * token and remove it.
+ *****************************************************************************/
+
+void RemoveEventFromArray(CFMutableArrayRef array, CFNumberRef launchdToken)
+{
+       CFIndex i;
+       CFIndex count = CFArrayGetCount(array);
+       // Loop thru looking for us.
+       for (i = 0; i < count; )
+       {
+               CFDictionaryRef eventDict = CFArrayGetValueAtIndex(array, i);
+               CFNumberRef token = CFDictionaryGetValue(eventDict, sLaunchdTokenKey);
+               
+               if (CFEqual(token, launchdToken)) // This is the same event?
+               {
+                       CFArrayRemoveValueAtIndex(array, i);    // Remove the event,
+                       break; // The token should only exist once, so it make no sense to continue.
+               }
+               else
+               {
+                       ++i; // If it's not us, advance.
+               }
+       }       
+}
+
+#pragma mark -
+#pragma mark Net Service Browser Stuff
+#pragma mark -
+
+/*****************************************************************************
+ * ServiceBrowserCallback
+ * - 
+ * This method is the heart of the plugin. It's the runloop callback annoucing 
+ * the appearence and disappearance of network services.
+ *****************************************************************************/
+
+void ServiceBrowserCallback (DNSServiceRef                             sdRef,
+                                                        DNSServiceFlags            flags,
+                                                        uint32_t                   interfaceIndex,
+                                                        DNSServiceErrorType        errorCode,
+                                                        const char*                serviceName,
+                                                        const char*                regtype,
+                                                        const char*                replyDomain,
+                                                        void*                      context )
+{
+       (void)interfaceIndex;
+       (void)regtype;
+       (void)replyDomain;
+       BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)context;
+       NetBrowserInfo* browser = BrowserForSDRef(plugin, sdRef);
+       
+       if (!browser) // Missing browser?
+               return;
+       
+       if (errorCode != kDNSServiceErr_NoError)
+               return;
+       
+       CFStringRef cfServiceName = CFStringCreateWithCString(NULL, serviceName, kCFStringEncodingUTF8);
+       
+       if (flags & kDNSServiceFlagsAdd)
+       {
+               HandleTemporaryEventsForService(plugin, browser, cfServiceName, plugin->_onAddEvents);
+               HandleStateEventsForService(plugin, browser, cfServiceName, true);
+       }
+       else 
+       {
+               HandleTemporaryEventsForService(plugin, browser, cfServiceName, plugin->_onRemoveEvents);
+               HandleStateEventsForService(plugin, browser, cfServiceName, false);
+       }
+
+       CFRelease(cfServiceName);
+}
+
+/*****************************************************************************
+ * HandleTemporaryEventsForService
+ * - 
+ * This method handles the firing of one shot events. Aka. Events that are 
+ * signaled when a service appears / disappears. They have a temporarly 
+ * signaled state. 
+ *****************************************************************************/
+void HandleTemporaryEventsForService(BonjourUserEventsPlugin* plugin, NetBrowserInfo* browser, CFStringRef serviceName, CFMutableDictionaryRef eventsDictionary)
+{
+       CFArrayRef events = (CFArrayRef)CFDictionaryGetValue(eventsDictionary, browser); // Get events for the browser we passed in.
+       CFIndex i;
+       CFIndex count;
+       
+       if (!events)  // Somehow we have a orphan browser...
+               return;
+       
+       count = CFArrayGetCount(events);
+       
+       // Go thru the events and run filters, notifity if they pass.
+       for (i = 0; i < count; ++i)
+       {
+               CFDictionaryRef eventDict = (CFDictionaryRef)CFArrayGetValueAtIndex(events, i);
+               CFStringRef eventServiceName = (CFStringRef)CFDictionaryGetValue(eventDict, sServiceNameKey);
+               CFNumberRef token = (CFNumberRef) CFDictionaryGetValue(eventDict, sLaunchdTokenKey);
+                               
+               // Currently we only filter on service name, that makes this as simple as... 
+               if (!eventServiceName || CFEqual(serviceName, eventServiceName))
+               {
+                       // Create Context Info
+                       CFRunLoopTimerContext context; 
+                       TimerContextInfo* info = TimerContextInfoCreate(plugin, token);
+
+                       context.version = 0;
+                       context.info = info;
+                       context.retain = TimerContextInfoRetain;
+                       context.release = TimerContextInfoRelease;
+                       context.copyDescription = TimerContextInfoCopyDescription;
+                       
+                       // Create and add one shot timer to flip the event off after a second
+                       CFRunLoopTimerRef timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + 1.0, 0, 0, 0, TemporaryEventTimerCallout, &context);
+                       CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes);
+                       
+                       // Signal Event
+                       UserEventAgentSetLaunchEventState(plugin->_pluginContext, token, true);
+                       
+                       // Clean Up
+                       TimerContextInfoRelease(info);
+                       CFRelease(timer);
+                       
+               }
+       }
+       
+}
+
+/*****************************************************************************
+ * HandleStateEventsForService
+ * - 
+ * This method handles the toggling the state of a while exists event to 
+ * reflect the network. 
+ *****************************************************************************/
+void HandleStateEventsForService(BonjourUserEventsPlugin* plugin, NetBrowserInfo* browser, CFStringRef serviceName, Boolean didAppear)
+{
+       CFArrayRef events = (CFArrayRef)CFDictionaryGetValue(plugin->_whileServiceExist, browser); // Get the _whileServiceExist events that are interested in this browser.
+       CFIndex i;
+       CFIndex count;
+       
+       if (!events)  // Somehow we have a orphan browser...
+               return;
+       
+       count = CFArrayGetCount(events);
+       
+       // Go thru the events and run filters, notifity if they pass.
+       for (i = 0; i < count; ++i)
+       {
+               CFDictionaryRef eventDict = (CFDictionaryRef)CFArrayGetValueAtIndex(events, i);
+               CFStringRef eventServiceName = (CFStringRef)CFDictionaryGetValue(eventDict, sServiceNameKey);
+               CFNumberRef token = (CFNumberRef) CFDictionaryGetValue(eventDict, sLaunchdTokenKey);
+               
+               // Currently we only filter on service name, that makes this as simple as... 
+               if (!eventServiceName || CFEqual(serviceName, eventServiceName))
+                       UserEventAgentSetLaunchEventState(plugin->_pluginContext, token, didAppear);
+       }
+}
+
+/*****************************************************************************
+ * TemporaryEventTimerCallout
+ * - 
+ * This method is invoked a second after a watched service appears / disappears
+ * to toggle the state of the launch event back to false.
+ *****************************************************************************/
+void TemporaryEventTimerCallout ( CFRunLoopTimerRef timer, void *info )
+{
+       TimerContextInfo* contextInfo = (TimerContextInfo*)info;
+
+       UserEventAgentSetLaunchEventState(contextInfo->plugin->_pluginContext, contextInfo->token, false);
+       
+       // Remove from pending timers array.
+       CFIndex i;
+       CFIndex count = CFArrayGetCount(contextInfo->plugin->_timers);
+       
+       for (i = 0; i < count; ++i)
+       {
+               CFRunLoopTimerRef item = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(contextInfo->plugin->_timers, i);
+               
+               if (item == timer)
+                       break;
+       }
+       
+       if (i != count)
+               CFArrayRemoveValueAtIndex(contextInfo->plugin->_timers, i);
+}
+
+#pragma mark -
+#pragma mark Convenence
+#pragma mark -
+
+/*****************************************************************************
+ * CStringFromCFString
+ * - 
+ * Silly convenence function for dealing with non-critical CFSTR -> cStr
+ * conversions.
+ *****************************************************************************/
+
+const char* CStringFromCFString(CFStringRef string)
+{
+       const char* defaultString = "??????";
+       const char* cstring;
+
+       if (!string)
+               return defaultString;
+       
+       cstring = CFStringGetCStringPtr(string, kCFStringEncodingUTF8);
+       
+       return (cstring) ? cstring : defaultString;
+       
+}
+
+#pragma mark -
+#pragma mark TimerContextInfo "Object"
+#pragma mark -
+
+/*****************************************************************************
+ * TimerContextInfoCreate
+ * - 
+ * Convenence for creating TimerContextInfo pseudo-objects
+ *****************************************************************************/
+TimerContextInfo* TimerContextInfoCreate(BonjourUserEventsPlugin* plugin, CFNumberRef token)
+{
+       TimerContextInfo* info = malloc(sizeof(TimerContextInfo));
+       
+       info->refCount = 1;
+       info->plugin = plugin;
+       info->token = (CFNumberRef)CFRetain(token);
+       
+       return info;
+}
+
+/*****************************************************************************
+ * TimerContextInfoRetain
+ * - 
+ * Convenence for retaining TimerContextInfo pseudo-objects
+ *****************************************************************************/
+const void* TimerContextInfoRetain(const void* info)
+{
+       TimerContextInfo* context = (TimerContextInfo*)info;
+       
+       if (!context)
+               return NULL;
+       
+       ++context->refCount;
+       
+       return context;
+}
+
+/*****************************************************************************
+ * TimerContextInfoRelease
+ * - 
+ * Convenence for releasing TimerContextInfo pseudo-objects
+ *****************************************************************************/
+void TimerContextInfoRelease(const void* info)
+{
+       TimerContextInfo* context = (TimerContextInfo*)info;
+       
+       if (!context)
+               return;
+       
+       if (context->refCount == 1)
+       {
+               CFRelease(context->token);
+               free(context);
+               return;
+       }
+       else 
+       {
+               --context->refCount;
+       }
+}
+
+/*****************************************************************************
+ * TimerContextInfoCopyDescription
+ * - 
+ * This method actually does nothing, but is just a stub so CF is happy.
+ *****************************************************************************/
+CFStringRef TimerContextInfoCopyDescription(const void* info)
+{
+       (void)info;
+       return CFStringCreateWithCString(NULL, "TimerContextInfo: No useful description", kCFStringEncodingUTF8);
+}
+
+
+#pragma mark -
+#pragma mark NetBrowserInfo "Object"
+#pragma mark -
+/*****************************************************************************
+ * NetBrowserInfoCreate
+ * - 
+ * The method creates a NetBrowserInfo Object and initalizes it.
+ *****************************************************************************/
+NetBrowserInfo* NetBrowserInfoCreate(CFStringRef serviceType, CFStringRef domain, void* context)
+{
+       NetBrowserInfo* outObj = NULL;
+       DNSServiceRef browserRef = NULL;
+       char* cServiceType = NULL;
+       char* cDomain = NULL;
+       Boolean success = true;
+       
+       CFIndex serviceSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(serviceType), kCFStringEncodingUTF8);
+       cServiceType = calloc(serviceSize, 1);
+       success = CFStringGetCString(serviceType, cServiceType, serviceSize, kCFStringEncodingUTF8);
+       
+       if (domain)
+       {
+               CFIndex domainSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(domain), kCFStringEncodingUTF8);
+               cDomain = calloc(serviceSize, 1);
+               success = success && CFStringGetCString(domain, cDomain, domainSize, kCFStringEncodingUTF8);
+       }
+       
+       if (!success)
+       {
+               fprintf(stderr, "LaunchEvent has badly encoded service type or domain.\n");
+               free(cServiceType);
+
+               if (cDomain)
+                       free(cDomain);
+               
+               return NULL;
+       }
+       
+       DNSServiceErrorType err = DNSServiceBrowse(&browserRef, 0, 0, cServiceType, cDomain, ServiceBrowserCallback, context);
+
+       if (err != kDNSServiceErr_NoError)
+       {
+               fprintf(stderr, "Failed to create browser for %s, %s\n", cServiceType, cDomain);
+               free(cServiceType);
+               
+               if (cDomain)
+                       free(cDomain);
+               
+               return NULL;
+       }
+
+       DNSServiceSetDispatchQueue(browserRef, dispatch_get_main_queue());
+       
+       
+       outObj = malloc(sizeof(NetBrowserInfo));
+       
+       outObj->refCount = 1;
+       outObj->browserRef = browserRef;
+
+       free(cServiceType);
+                                                                 
+       if (cDomain)
+               free(cDomain);
+       
+       return outObj;
+}
+
+/*****************************************************************************
+ * NetBrowserInfoRetain
+ * - 
+ * The method retains a NetBrowserInfo object.
+ *****************************************************************************/
+const void* NetBrowserInfoRetain(CFAllocatorRef allocator, const void* info)
+{
+       (void)allocator;
+       NetBrowserInfo* obj = (NetBrowserInfo*)info;
+       
+       if (!obj) 
+               return NULL;
+       
+       ++obj->refCount;
+       
+       return obj;
+}
+
+/*****************************************************************************
+ * NetBrowserInfoRelease
+ * - 
+ * The method releases a NetBrowserInfo object.
+ *****************************************************************************/
+void NetBrowserInfoRelease(CFAllocatorRef allocator, const void* info)
+{
+       (void)allocator;
+       NetBrowserInfo* obj = (NetBrowserInfo*)info;
+       
+       if (!obj)
+               return;
+       
+       if (obj->refCount == 1)
+       {
+               DNSServiceRefDeallocate(obj->browserRef);
+               free(obj);
+       }
+       else 
+       {
+               --obj->refCount;
+       }
+
+}
+
+/*****************************************************************************
+ * NetBrowserInfoEqual
+ * - 
+ * The method is used to compare two NetBrowserInfo objects for equality.
+ *****************************************************************************/
+Boolean        NetBrowserInfoEqual(const void *value1, const void *value2)
+{
+       NetBrowserInfo* obj1 = (NetBrowserInfo*)value1;
+       NetBrowserInfo* obj2 = (NetBrowserInfo*)value2;
+       
+       if (obj1->browserRef == obj2->browserRef)
+               return true;
+       
+       return false;
+}
+
+/*****************************************************************************
+ * NetBrowserInfoHash
+ * - 
+ * The method is used to make a hash for the object. We can cheat and use the 
+ * browser pointer.
+ *****************************************************************************/
+CFHashCode     NetBrowserInfoHash(const void *value)
+{
+       return (CFHashCode)((NetBrowserInfo*)value)->browserRef;
+}
+
+
+/*****************************************************************************
+ * NetBrowserInfoCopyDescription
+ * - 
+ * Make CF happy.
+ *****************************************************************************/
+CFStringRef    NetBrowserInfoCopyDescription(const void *value)
+{
+       (void)value;
+       return CFStringCreateWithCString(NULL, "NetBrowserInfo: No useful description", kCFStringEncodingUTF8);
+}
\ No newline at end of file
diff --git a/mDNSMacOSX/P2PPacketFilter.c b/mDNSMacOSX/P2PPacketFilter.c
new file mode 100644 (file)
index 0000000..c61c7b1
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ *
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <net/if.h>
+#include <System/net/pfvar.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <AssertMacros.h>
+#include "P2PPacketFilter.h"
+
+#define AIRDROP_ANCHOR_PATH    "com.apple/200.AirDrop"
+#define MDNS_ANCHOR_NAME       "Bonjour"
+#define MDNS_ANCHOR_PATH       AIRDROP_ANCHOR_PATH "/" MDNS_ANCHOR_NAME
+
+#define PF_DEV_PATH "/dev/pf"
+#define BONJOUR_PORT 5353
+
+static int openPFDevice( int * outFD )
+{
+    int err;
+    int fd = open( PF_DEV_PATH, O_RDWR );
+    
+    if( fd >= 0 )
+    {
+        err = 0;
+        *outFD = fd;
+    }
+    else
+    {
+        err = errno;
+    }
+    
+    return err;
+}
+
+static int getTicket( int devFD, u_int32_t * outTicket, char * anchorPath )
+{
+    struct pfioc_trans_e trans_e;
+    
+    trans_e.rs_num = PF_RULESET_FILTER;
+    strlcpy( trans_e.anchor, anchorPath, sizeof( trans_e.anchor ) );
+    
+    struct pfioc_trans trans;
+    
+    trans.size = 1;
+    trans.esize = sizeof( trans_e );
+    trans.array = &trans_e;
+    
+    int result, ioctlError;
+    
+    ioctlError = ioctl( devFD, DIOCXBEGIN, &trans );
+    if( ioctlError )
+    {
+        result = errno;
+    }
+    else
+    {
+        result = 0;
+        *outTicket = trans_e.ticket;
+    }
+    
+    return result;
+}
+
+static int commitChange( int devFD, u_int32_t ticket, char * anchorPath )
+{
+    struct pfioc_trans_e trans_e;
+    
+    trans_e.rs_num = PF_RULESET_FILTER;
+    strlcpy( trans_e.anchor, anchorPath, sizeof( trans_e.anchor ) );
+    trans_e.ticket = ticket;
+    
+    struct pfioc_trans trans;
+    
+    trans.size = 1;
+    trans.esize = sizeof( trans_e );
+    trans.array = &trans_e;
+    
+    int result, ioctlError;
+    
+    ioctlError = ioctl( devFD, DIOCXCOMMIT, &trans );
+    if( ioctlError )
+        result = errno;
+    else
+        result = 0;
+    
+    return result;
+}
+
+static int getPoolTicket( int devFD, u_int32_t * outPoolTicket )
+{
+    struct pfioc_pooladdr pp;
+    
+    int result, ioctlError;
+    
+    ioctlError = ioctl( devFD, DIOCBEGINADDRS, &pp );
+    if( ioctlError )
+    {
+        result = errno;
+    }
+    else
+    {
+        result = 0;
+        *outPoolTicket = pp.ticket;
+    }
+    
+    return result;
+}
+
+static int addRule( int devFD, struct pfioc_rule * pr )
+{
+    int result, ioctlResult;
+    
+    ioctlResult = ioctl( devFD, DIOCADDRULE, pr );
+    if( ioctlResult )
+        result = errno;
+    else
+        result = 0;
+    
+    return result;
+}
+
+static void initRuleHeader( struct pfioc_rule * pr,
+                             u_int32_t ticket,
+                             u_int32_t poolTicket,
+                                                        char * anchorPath )
+{
+    pr->action = PF_CHANGE_NONE;
+    pr->ticket = ticket;
+    pr->pool_ticket = poolTicket;
+    strlcpy( pr->anchor, anchorPath, sizeof( pr->anchor ) );
+}
+
+// allow inbound traffice on the Bonjour port (5353) 
+static void initBonjourRule( struct pfioc_rule * pr,
+                              const char * interfaceName,
+                              u_int32_t ticket,
+                              u_int32_t poolTicket, 
+                                                         char * anchorPath )
+{
+    memset( pr, 0, sizeof( *pr ) );
+    
+    // Header
+    initRuleHeader( pr, ticket, poolTicket, anchorPath );
+    
+    // Rule
+    pr->rule.dst.xport.range.port[0] = htons( BONJOUR_PORT );
+    pr->rule.dst.xport.range.op = PF_OP_EQ;
+    
+    strlcpy( pr->rule.ifname, interfaceName, sizeof( pr->rule.ifname ) );
+    
+    pr->rule.action = PF_PASS;
+    pr->rule.direction = PF_IN;
+    pr->rule.keep_state = 1;
+    pr->rule.af = AF_INET6;
+    pr->rule.proto = IPPROTO_UDP;
+    pr->rule.extfilter = PF_EXTFILTER_APD;
+}
+
+// allow outbound TCP connections and return traffic for those connections
+static void initOutboundTCPRule( struct pfioc_rule * pr,
+                             const char * interfaceName,
+                             u_int32_t ticket,
+                             u_int32_t poolTicket, 
+                                                        char * anchorPath )
+{
+    memset( pr, 0, sizeof( *pr ) );
+    
+    // Header
+    initRuleHeader( pr, ticket, poolTicket, anchorPath );
+    
+    // Rule
+    strlcpy( pr->rule.ifname, interfaceName, sizeof( pr->rule.ifname ) );
+    
+    pr->rule.action = PF_PASS;
+    pr->rule.direction = PF_OUT;
+    pr->rule.keep_state = 1;
+    pr->rule.proto = IPPROTO_TCP;
+}
+
+// allow inbound traffic on the specified port and protocol
+static void initPortRule( struct pfioc_rule * pr,
+                              const char * interfaceName,
+                              u_int32_t ticket,
+                              u_int32_t poolTicket,
+                                                         char * anchorPath,
+                                                         u_int16_t port,
+                                                         u_int16_t protocol )
+{
+    memset( pr, 0, sizeof( *pr ) );
+    
+    // Header
+    initRuleHeader( pr, ticket, poolTicket, anchorPath );
+    
+    // Rule
+       // mDNSResponder passes the port in Network Byte Order, so htons(port) is not required 
+    pr->rule.dst.xport.range.port[0] = port;
+    pr->rule.dst.xport.range.op = PF_OP_EQ;
+    
+    strlcpy( pr->rule.ifname, interfaceName, sizeof( pr->rule.ifname ) );
+    
+    pr->rule.action = PF_PASS;
+    pr->rule.direction = PF_IN;
+    pr->rule.keep_state = 1;
+    pr->rule.af = AF_INET6;
+    pr->rule.proto = protocol;
+    pr->rule.extfilter = PF_EXTFILTER_APD;
+}
+
+// allow inbound traffice on the Bonjour port (5353) and the specified port and protocol
+int P2PPacketFilterAddBonjourRuleSet(const char * interfaceName, u_int16_t port, u_int16_t protocol )
+{
+    int result;
+    u_int32_t ticket, poolTicket;
+    int devFD = -1;
+       char * anchorPath = MDNS_ANCHOR_PATH;
+       
+    result = openPFDevice( &devFD );
+    require( result == 0, exit );
+    
+    result = getTicket( devFD, &ticket, anchorPath );
+    require( result == 0, exit );
+    
+    result = getPoolTicket( devFD, &poolTicket );
+    require( result == 0, exit );
+    
+    struct pfioc_rule pr;
+    
+    // allow inbound Bonjour traffice to port 5353 
+    initBonjourRule( &pr, interfaceName, ticket, poolTicket, anchorPath);
+    
+    result = addRule( devFD, &pr );
+    require( result == 0, exit );
+    
+    // open inbound port for service
+    initPortRule( &pr, interfaceName, ticket, poolTicket, anchorPath, port, protocol );
+    
+    result = addRule( devFD, &pr );
+    require( result == 0, exit );
+    
+    // allow outbound TCP connections and return traffic for those connections
+    initOutboundTCPRule( &pr, interfaceName, ticket, poolTicket, anchorPath);
+    
+    result = addRule( devFD, &pr );
+    require( result == 0, exit );
+    
+    result = commitChange( devFD, ticket, anchorPath );
+    require( result == 0, exit );
+    
+exit:
+    
+    if( devFD >= 0 )
+        close( devFD );
+    
+    return result;
+}
+
+int P2PPacketFilterClearBonjourRules()
+{
+       int result;
+       int pfDev = -1;
+       u_int32_t ticket;
+       char * anchorPath = MDNS_ANCHOR_PATH;
+       
+       result = openPFDevice( &pfDev );
+       require( result == 0, exit );
+       
+       result = getTicket( pfDev, &ticket, anchorPath );
+       require( result == 0, exit );
+       
+       result = commitChange( pfDev, ticket, anchorPath );
+       
+exit:
+       
+       if( pfDev >= 0 )
+               close( pfDev );
+
+       return result;
+}
+
diff --git a/mDNSMacOSX/P2PPacketFilter.h b/mDNSMacOSX/P2PPacketFilter.h
new file mode 100644 (file)
index 0000000..81bd3df
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ *
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _P2P_PACKET_FILTER_H_
+#define _P2P_PACKET_FILTER_H_
+
+enum {
+       PF_SET_RULES,
+       PF_CLEAR_RULES
+};
+
+int P2PPacketFilterAddBonjourRuleSet(const char * interfaceName, u_int16_t port, u_int16_t protocol );
+int P2PPacketFilterClearBonjourRules(void);
+
+#endif /* _P2P_PACKET_FILTER_H_ */
index 62d2f78372312df194518f0cf56201064cdb81d2..529a90c09b8e0b7830c25bc863bd45f6346b6747 100644 (file)
@@ -84,12 +84,12 @@ static CacheEntity rrcachestorage[RR_CACHE_SIZE];
 static const char kmDNSBootstrapName[] = "com.apple.mDNSResponderRestart";
 static mach_port_t m_port            = MACH_PORT_NULL;
 
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 mDNSlocal void PrepareForIdle(void *m_param);
-#else __LIB_DISPATCH__
+#else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 static mach_port_t client_death_port = MACH_PORT_NULL;
 static mach_port_t signal_port       = MACH_PORT_NULL;
-#endif __LIB_DISPATCH__
+#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 
 static mach_port_t server_priv_port  = MACH_PORT_NULL;
 
@@ -109,6 +109,7 @@ static int started_via_launchdaemon = 0;    // Indicates we're running on Tiger or
 static mDNSBool advertise = mDNS_Init_AdvertiseLocalAddresses; // By default, advertise addresses (& other records) via multicast
 
 extern mDNSBool StrictUnicastOrdering;
+extern mDNSBool AlwaysAppendSearchDomains;
 
 //*************************************************************************************************************
 #if COMPILER_LIKES_PRAGMA_MARK
@@ -547,7 +548,7 @@ mDNSlocal mDNSBool CheckForExistingClient(mach_port_t c)
        return(e || b || l || r);
        }
 
-#ifndef __LIB_DISPATCH__
+#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 
 mDNSlocal void ClientDeathCallback(CFMachPortRef unusedport, void *voidmsg, CFIndex size, void *info)
        {
@@ -567,11 +568,11 @@ mDNSlocal void ClientDeathCallback(CFMachPortRef unusedport, void *voidmsg, CFIn
        KQueueUnlock(&mDNSStorage, "Mach AbortClient");
        }
 
-#endif __LIB_DISPATCH__
+#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 
 mDNSlocal void EnableDeathNotificationForClient(mach_port_t ClientMachPort, void *m)
        {
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
        dispatch_source_t mach_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND, ClientMachPort, 0, dispatch_get_main_queue());
        if (mach_source == mDNSNULL)
                {
@@ -582,14 +583,14 @@ mDNSlocal void EnableDeathNotificationForClient(mach_port_t ClientMachPort, void
                mach_port_destroy(mach_task_self(), ClientMachPort);
                });
        dispatch_resume(mach_source);
-#else __LIB_DISPATCH__
+#else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
        mach_port_t prev;
        kern_return_t r = mach_port_request_notification(mach_task_self(), ClientMachPort, MACH_NOTIFY_DEAD_NAME, 0,
                                                                                                         client_death_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev);
        // If the port already died while we were thinking about it, then abort the operation right away
        if (r != KERN_SUCCESS)
                AbortClientWithLogMessage(ClientMachPort, "died/deallocated before we could enable death notification", "", m);
-#endif __LIB_DISPATCH__
+#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
        }
 
 //*************************************************************************************************************
@@ -1077,7 +1078,7 @@ mDNSlocal mStatus AddServiceInstance(DNSServiceRegistration *x, const domainname
        si->domain          = *domain;
 
        err = mDNS_RegisterService(&mDNSStorage, &si->srs, &si->name, &x->type, domain, NULL,
-               x->port, x->txtinfo, x->txt_len, SubTypes, x->NumSubTypes, mDNSInterface_Any, RegCallback, si);
+               x->port, x->txtinfo, x->txt_len, SubTypes, x->NumSubTypes, mDNSInterface_Any, RegCallback, si, 0);
        if (!err)
                {
                si->next = x->regs;
@@ -1401,7 +1402,7 @@ mDNSexport kern_return_t provide_DNSServiceRegistrationAddRecord_rpc(mach_port_t
                // Do the operation
                LogOperation("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) REF %p",
                                         client, si->srs.RR_SRV.resrec.name->c, type, data_len, extra);
-               err = mDNS_AddRecordToService(&mDNSStorage, &si->srs, extra, &extra->r.rdatastorage, ttl);
+               err = mDNS_AddRecordToService(&mDNSStorage, &si->srs, extra, &extra->r.rdatastorage, ttl, 0);
 
                if (err)
                        {
@@ -1750,35 +1751,32 @@ mDNSlocal void ExitCallback(int sig)
        mDNS_StartExit(&mDNSStorage);
        }
 
-#ifndef __LIB_DISPATCH__
+#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 
 // Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit
 mDNSlocal void HandleSIG(int sig)
        {
-       // WARNING: can't call syslog or fprintf from signal handler
+       kern_return_t status;
        mach_msg_header_t header;
+
+       // WARNING: can't call syslog or fprintf from signal handler
        header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
        header.msgh_remote_port = signal_port;
        header.msgh_local_port = MACH_PORT_NULL;
        header.msgh_size = sizeof(header);
        header.msgh_id = sig;
-       if (mach_msg_send(&header) != MACH_MSG_SUCCESS)
-               if (sig == SIGTERM || sig == SIGINT) exit(-1);
-       }
 
-mDNSlocal void CatchABRT(int sig)
-       {
-       // WARNING: can't call syslog or fprintf from signal handler
-       // We want a CrashReporter stack trace so we can find out what library called abort()
-       // So that we will crash, unblock all signals (that abort() may have blocked)
-       sigset_t mask;
-       sigfillset(&mask);
-       sigprocmask(SIG_UNBLOCK, &mask, NULL);
-       (void)sig;
-       while(1) *(long*)0 = 0;
+       status = mach_msg(&header, MACH_SEND_MSG | MACH_SEND_TIMEOUT, header.msgh_size,
+               0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
+
+       if (status != MACH_MSG_SUCCESS)
+               {
+               if (status == MACH_SEND_TIMED_OUT) mach_msg_destroy(&header);
+               if (sig == SIGTERM || sig == SIGINT) exit(-1);
+               }
        }
 
-#endif __LIB_DISPATCH__
+#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 
 mDNSlocal void INFOCallback(void)
        {
@@ -1789,8 +1787,9 @@ mDNSlocal void INFOCallback(void)
        DNSServiceRegistration      *r;
        NetworkInterfaceInfoOSX     *i;
        DNSServer *s;
+       McastResolver *mr;
 
-       LogMsg("---- BEGIN STATE LOG ---- %s", mDNSResponderVersionString);
+       LogMsg("---- BEGIN STATE LOG ---- %s %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers);
        
        udsserver_info(&mDNSStorage);
 
@@ -1840,16 +1839,16 @@ mDNSlocal void INFOCallback(void)
                        {
                        // Allow six characters for interface name, for names like "vmnet8"
                        if (!i->Exists)
-                               LogMsgNoIdent("%p (%p), Registered %p,  %s %-6s(%lu) %.6a %.6a %#-14a dormant for %d seconds",
-                                       i->ifinfo.InterfaceID, i, i->Registered,
+                               LogMsgNoIdent("%p %2ld, Registered %p,  %s %-6s(%lu) %.6a %.6a %#-14a dormant for %d seconds",
+                                       i, i->ifinfo.InterfaceID, i->Registered,
                                        i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifinfo.ifname, i->scope_id, &i->ifinfo.MAC, &i->BSSID,
                                        &i->ifinfo.ip, utc - i->LastSeen);
                        else
                                {
                                const CacheRecord *sps[3];
                                FindSPSInCache(&mDNSStorage, &i->ifinfo.NetWakeBrowse, sps);
-                               LogMsgNoIdent("%p (%p), Registered %p,  %s %-6s(%lu) %.6a %.6a %s %s %-15.4a %s %s %s %s %#a",
-                                       i->ifinfo.InterfaceID, i, i->Registered,
+                               LogMsgNoIdent("%p %2ld, Registered %p,  %s %-6s(%lu) %.6a %.6a %s %s %-15.4a %s %s %s %s %#a",
+                                       i, i->ifinfo.InterfaceID, i->Registered,
                                        i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifinfo.ifname, i->scope_id, &i->ifinfo.MAC, &i->BSSID,
                                        i->ifinfo.InterfaceActive ? "Active" : "      ",
                                        i->ifinfo.IPv4Available ? "v4" : "  ",
@@ -1874,9 +1873,10 @@ mDNSlocal void INFOCallback(void)
                for (s = mDNSStorage.DNSServers; s; s = s->next)
                        {
                        NetworkInterfaceInfoOSX *ifx = IfindexToInterfaceInfoOSX(&mDNSStorage, s->interface);
-                       LogMsgNoIdent("DNS Server %##s %s%s%#a:%d %d %s %s",
+                       LogMsgNoIdent("DNS Server %##s %s%s%#a:%d %d %s %d %s",
                                s->domain.c, ifx ? ifx->ifinfo.ifname : "", ifx ? " " : "", &s->addr, mDNSVal16(s->port),
                                s->penaltyTime ? s->penaltyTime - mDNS_TimeNow(&mDNSStorage) : 0, s->scoped ? "Scoped" : "",
+                               s->timeout,
                                s->teststate == DNSServer_Untested ? "(Untested)" :
                                s->teststate == DNSServer_Passed   ? ""           :
                                s->teststate == DNSServer_Failed   ? "(Failed)"   :
@@ -1884,13 +1884,21 @@ mDNSlocal void INFOCallback(void)
                        }
                }
 
+       LogMsgNoIdent("--------- Mcast Resolvers ----------");
+       if (!mDNSStorage.McastResolvers) LogMsgNoIdent("<None>");
+       else
+               {
+               for (mr = mDNSStorage.McastResolvers; mr; mr = mr->next)
+                       LogMsgNoIdent("Mcast Resolver %##s timeout %u", mr->domain.c, mr->timeout);
+               }
+
        mDNSs32 now = mDNS_TimeNow(&mDNSStorage);
        LogMsgNoIdent("Timenow 0x%08lX (%d)", (mDNSu32)now, now);
 
-       LogMsg("----  END STATE LOG  ---- %s", mDNSResponderVersionString);
+       LogMsg("----  END STATE LOG  ---- %s %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers);
        }
 
-#ifndef __LIB_DISPATCH__
+#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 
 mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
        {
@@ -1946,8 +1954,7 @@ mDNSlocal kern_return_t mDNSDaemonInitialize(void)
                {
                s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL);
                m_port = CFMachPortGetPort(s_port);
-               char *MachServerName = OSXVers < OSXVers_10_3_Panther ? "DNSServiceDiscoveryServer" : "com.apple.mDNSResponder";
-               kern_return_t status = bootstrap_register(bootstrap_port, MachServerName, m_port);
+               kern_return_t status = bootstrap_register(bootstrap_port, "com.apple.mDNSResponder", m_port);
        
                if (status)
                        {
@@ -1988,13 +1995,13 @@ mDNSlocal kern_return_t mDNSDaemonInitialize(void)
        return(err);
        }
 
-#else __LIB_DISPATCH__
+#else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 
 // SignalDispatch is mostly just a copy/paste of entire code block from SignalCallback above.
 // The common code should be a subroutine, or we end up having to fix bugs in two places all the time.
 // The same applies to mDNSDaemonInitialize, much of which is just a copy/paste of chunks
 // of code from above. Alternatively we could remove the duplicated source code by having
-// single routines, with the few differing parts bracketed with "#ifndef __LIB_DISPATCH__"
+// single routines, with the few differing parts bracketed with "#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM"
 
 mDNSlocal void SignalDispatch(dispatch_source_t source)
        {
@@ -2062,8 +2069,7 @@ mDNSlocal kern_return_t mDNSDaemonInitialize(void)
                {
                s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL);
                m_port = CFMachPortGetPort(s_port);
-               char *MachServerName = OSXVers < OSXVers_10_3_Panther ? "DNSServiceDiscoveryServer" : "com.apple.mDNSResponder";
-               kern_return_t status = bootstrap_register(bootstrap_port, MachServerName, m_port);
+               kern_return_t status = bootstrap_register(bootstrap_port, "com.apple.mDNSResponder", m_port);
        
                if (status)
                        {
@@ -2125,13 +2131,25 @@ mDNSlocal kern_return_t mDNSDaemonInitialize(void)
        return(err);
        }
 
-#endif __LIB_DISPATCH__
+#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 
 mDNSlocal mDNSs32 mDNSDaemonIdle(mDNS *const m)
        {
        mDNSs32 now = mDNS_TimeNow(m);
 
-       // 1. If we have network change events to handle, do them FIRST, before calling mDNS_Execute()
+       // 1. If we need to set domain secrets, do so before handling the network change
+       // Detailed reason:
+       // BTMM domains listed in DynStore Setup:/Network/BackToMyMac are added to the registration domains list,
+       // and we need to setup the associated AutoTunnel DomainAuthInfo entries before that happens.
+       if (m->p->KeyChainTimer && now - m->p->KeyChainTimer >= 0)
+               {
+               m->p->KeyChainTimer = 0;
+               mDNS_Lock(m);
+               SetDomainSecrets(m);
+               mDNS_Unlock(m);
+               }
+
+       // 2. If we have network change events to handle, do them before calling mDNS_Execute()
        // Detailed reason:
        // mDNSMacOSXNetworkChanged() currently closes and re-opens its sockets. If there are received packets waiting, they are lost.
        // mDNS_Execute() generates packets, including multicasts that are looped back to ourself.
@@ -2141,36 +2159,22 @@ mDNSlocal mDNSs32 mDNSDaemonIdle(mDNS *const m)
 
        if (m->p->RequestReSleep && now - m->p->RequestReSleep >= 0) { m->p->RequestReSleep = 0; mDNSPowerRequest(0, 0); }
 
-       // KeyChain frequently fails to notify clients of change events. To work around this
-       // we set a timer and periodically poll to detect if any changes have occurred.
-       // Without this Back To My Mac just does't work for a large number of users.
-       // See <rdar://problem/5124399> Not getting Keychain Changed events when enabling BTMM
-       if (m->p->KeyChainBugTimer && now - m->p->KeyChainBugTimer >= 0)
-               {
-               m->p->KeyChainBugInterval *= 2;
-               m->p->KeyChainBugTimer = NonZeroTime(now + m->p->KeyChainBugInterval);
-               if (m->p->KeyChainBugInterval > 2 * mDNSPlatformOneSecond) m->p->KeyChainBugTimer = 0;
-               mDNS_Lock(m);
-               SetDomainSecrets(m);
-               mDNS_Unlock(m);
-               }
-
-       // 2. Call mDNS_Execute() to let mDNSCore do what it needs to do
+       // 3. Call mDNS_Execute() to let mDNSCore do what it needs to do
        mDNSs32 nextevent = mDNS_Execute(m);
 
        if (m->p->NetworkChanged)
                if (nextevent - m->p->NetworkChanged > 0)
                        nextevent = m->p->NetworkChanged;
 
-       if (m->p->KeyChainBugTimer)
-               if (nextevent - m->p->KeyChainBugTimer > 0)
-                       nextevent = m->p->KeyChainBugTimer;
+       if (m->p->KeyChainTimer)
+               if (nextevent - m->p->KeyChainTimer > 0)
+                       nextevent = m->p->KeyChainTimer;
 
        if (m->p->RequestReSleep)
                if (nextevent - m->p->RequestReSleep > 0)
                        nextevent = m->p->RequestReSleep;
 
-       // 3. Deliver any waiting browse messages to clients
+       // 4. Deliver any waiting browse messages to clients
        DNSServiceBrowser *b = DNSServiceBrowserList;
 
        while (b)
@@ -2403,32 +2407,38 @@ mDNSlocal mDNSBool AllowSleepNow(mDNS *const m, mDNSs32 now)
        
                                if (result == kIOReturnNotReady)
                                        {
-                                       LogMsg("Requested wakeup in %d seconds unsuccessful; retrying with longer intervals", interval);
+                                       int r;
+                                       LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful; retrying with longer intervals", interval);
                                        // IOPMSchedulePowerEvent fails with kIOReturnNotReady (-536870184/0xe00002d8) if the
                                        // requested wake time is "too soon", but there's no API to find out what constitutes
                                        // "too soon" on any given OS/hardware combination, so if we get kIOReturnNotReady
                                        // we just have to iterate with successively longer intervals until it doesn't fail.
-                                       // Additionally, if our power request is deemed "too soon" for the machine to get to
-                                       // sleep and wake back up again, we attempt to cancel the sleep request, since the
-                                       // implication is that the system won't manage to be awake again at the time we need it.
+                                       // We preserve the value of "result" because if our original power request was deemed "too soon"
+                                       // for the machine to get to sleep and wake back up again, we attempt to cancel the sleep request,
+                                       // since the implication is that the system won't manage to be awake again at the time we need it.
                                        do
                                                {
                                                interval += (interval < 20) ? 1 : ((interval+3) / 4);
-                                               result = mDNSPowerRequest(1, interval);
+                                               r = mDNSPowerRequest(1, interval);
                                                }
-                                       while (result == kIOReturnNotReady);
+                                       while (r == kIOReturnNotReady);
+                                       if (r) LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful: %d %X", interval, r, r);
+                                       else LogSPS("AllowSleepNow: Requested later wakeup in %d seconds; will also attempt IOCancelPowerChange", interval);
+                                       }
+                               else
+                                       {
+                                       if (result) LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful: %d %X", interval, result, result);
+                                       else LogSPS("AllowSleepNow: Requested wakeup in %d seconds", interval);
                                        }
-       
-                               if (result) LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful: %d %X", interval, result, result);
-                               else LogSPS("AllowSleepNow: Requested wakeup in %d seconds", interval);
                                m->p->WakeAtUTC = mDNSPlatformUTC() + interval;
                                }
                        }
        
-               // Clear our interface list to empty state, ready to go to sleep
-               // As a side effect of doing this, we'll also cancel any outstanding SPS Resolve calls that didn't complete
                m->SleepState = SleepState_Sleeping;
-               mDNSMacOSXNetworkChanged(m);
+               // We used to clear our interface list to empty state here before going to sleep.
+               // The applications that try to connect to an external server during maintenance wakes, saw
+               // DNS resolution errors as we don't have any interfaces (most queries use SuppressUnusable
+               // flag). Thus, we don't remove our interfaces anymore on sleep.
                }
 
        LogSPS("AllowSleepNow: %s(%lX) %s at %ld (%d ticks remaining)",
@@ -2451,7 +2461,7 @@ mDNSlocal mDNSBool AllowSleepNow(mDNS *const m, mDNSs32 now)
        return(mDNStrue);
        }
 
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 
 mDNSexport void TriggerEventCompletion()
        {
@@ -2527,7 +2537,7 @@ mDNSlocal void PrepareForIdle(void *m_param)
        return;
        }
 
-#else __LIB_DISPATCH__
+#else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 
 mDNSlocal void KQWokenFlushBytes(int fd, __unused short filter, __unused void *context)
        {
@@ -2680,7 +2690,7 @@ mDNSlocal void * KQueueLoop(void *m_param)
        return NULL;
        }
 
-#endif __LIB_DISPATCH__
+#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 
 mDNSlocal void LaunchdCheckin(void)
        {
@@ -2796,7 +2806,8 @@ mDNSexport int main(int argc, char **argv)
        int i;
        kern_return_t status;
 
-       LogMsg("%s starting", mDNSResponderVersionString);
+       mDNSMacOSXSystemBuildNumber(NULL);
+       LogMsg("%s starting %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers);
        
 #if 0
        LogMsg("CacheRecord %d", sizeof(CacheRecord));
@@ -2824,33 +2835,28 @@ mDNSexport int main(int argc, char **argv)
                if (!strcasecmp(argv[i], "-DebugLogging"             )) mDNS_LoggingEnabled       = mDNStrue;
                if (!strcasecmp(argv[i], "-UnicastPacketLogging"     )) mDNS_PacketLoggingEnabled = mDNStrue;
                if (!strcasecmp(argv[i], "-OfferSleepProxyService"   ))
-                       OfferSleepProxyService = (i+1<argc && mDNSIsDigit(argv[i+1][0]) && mDNSIsDigit(argv[i+1][1]) && argv[i+1][2]==0) ? atoi(argv[++i]) : 80;
-               if (!strcasecmp(argv[i], "-StrictUnicastOrdering"    )) StrictUnicastOrdering = mDNStrue;
+                       OfferSleepProxyService = (i+1 < argc && mDNSIsDigit(argv[i+1][0]) && mDNSIsDigit(argv[i+1][1]) && argv[i+1][2]==0) ? atoi(argv[++i]) : 100;
+               if (!strcasecmp(argv[i], "-UseInternalSleepProxy"    ))
+                       UseInternalSleepProxy = (i+1<argc && mDNSIsDigit(argv[i+1][0]) && argv[i+1][1]==0) ? atoi(argv[++i]) : 1;
+               if (!strcasecmp(argv[i], "-StrictUnicastOrdering"    )) StrictUnicastOrdering     = mDNStrue;
                if (!strcasecmp(argv[i], "-DisableInboundRelay"      )) DisableInboundRelayConnection = mDNStrue;
+               if (!strcasecmp(argv[i], "-AlwaysAppendSearchDomains")) AlwaysAppendSearchDomains = mDNStrue;
                }
        
        // Note that mDNSPlatformInit will set DivertMulticastAdvertisements in the mDNS structure
        if (!advertise) LogMsg("Administratively prohibiting multicast advertisements");
 
-       OSXVers = mDNSMacOSXSystemBuildNumber(NULL);
-
-#ifndef __LIB_DISPATCH__
+#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 
        signal(SIGHUP,  HandleSIG);             // (Debugging) Purge the cache to check for cache handling bugs
        signal(SIGINT,  HandleSIG);             // Ctrl-C: Detach from Mach BootstrapService and exit cleanly
-       // On 10.5 and later, the default action for SIGABRT is to generate a crash report, so we only need our CatchABRT handler on 10.4
-       if (OSXVers <= OSXVers_10_4_Tiger)
-               {
-               LogInfo("Adding SIGABRT handler");
-               signal(SIGABRT, CatchABRT); // For debugging -- SIGABRT should never happen
-               }
        signal(SIGPIPE, SIG_IGN  );             // Don't want SIGPIPE signals -- we'll handle EPIPE errors directly
        signal(SIGTERM, HandleSIG);             // Machine shutting down: Detach from and exit cleanly like Ctrl-C
        signal(SIGINFO, HandleSIG);             // (Debugging) Write state snapshot to syslog
        signal(SIGUSR1, HandleSIG);             // (Debugging) Enable Logging
        signal(SIGUSR2, HandleSIG);             // (Debugging) Enable Packet Logging
 
-#endif __LIB_DISPATCH__
+#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 
        mDNSStorage.p = &PlatformStorage;       // Make sure mDNSStorage.p is set up, because validatelists uses it
        LaunchdCheckin();
@@ -2872,7 +2878,7 @@ mDNSexport int main(int argc, char **argv)
                        }
                }
 
-#ifndef __LIB_DISPATCH__
+#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 
        // Create the kqueue, mutex and thread to support KQSockets
        KQueueFD = kqueue();
@@ -2893,7 +2899,7 @@ mDNSexport int main(int argc, char **argv)
        PlatformStorage.WakeKQueueLoopFD = fdpair[0];
        KQueueSet(fdpair[1], EV_ADD, EVFILT_READ, &wakeKQEntry);
 
-#endif __LIB_DISPATCH__
+#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
        
        // Invoke sandbox profile /usr/share/sandbox/mDNSResponder.sb
 #if MDNS_NO_SANDBOX
@@ -2904,14 +2910,19 @@ mDNSexport int main(int argc, char **argv)
        else
                {
                char *sandbox_msg;
-               struct stat s;
                uint64_t sandbox_flags = SANDBOX_NAMED;
 
+               // On Desktop, the sandbox profile always exists under /usr/share/sandbox, no need to
+               // check it. stat results in calls to opendirectoryd and to avoid deadlocks with
+               // opendirectoryd early on, we avoid this call.
+#if TARGET_OS_EMBEDDED
+               struct stat s;
                if (stat("/usr/share/sandbox/mDNSResponder.sb", &s) == 0)
                        {
                        sandbox_flags = SANDBOX_NAMED_EXTERNAL;
                        LogInfo("Will load Sandbox profile from filesystem");
                        }
+#endif
 
                int sandbox_err = sandbox_init("mDNSResponder", sandbox_flags, &sandbox_msg);
                if (sandbox_err) { LogMsg("WARNING: sandbox_init error %s", sandbox_msg); sandbox_free_error(sandbox_msg); }
@@ -2927,11 +2938,11 @@ mDNSexport int main(int argc, char **argv)
 
        mDNSMacOSXNetworkChanged(&mDNSStorage);
 
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
        LogInfo("Daemon Start: Using LibDispatch");
        // CFRunLoopRun runs both CFRunLoop sources and dispatch sources
        CFRunLoopRun();
-#else __LIB_DISPATCH__
+#else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
        // Start the kqueue thread
        pthread_t KQueueThread;
        i = pthread_create(&KQueueThread, NULL, KQueueLoop, &mDNSStorage);
@@ -2942,7 +2953,7 @@ mDNSexport int main(int argc, char **argv)
                LogMsg("ERROR: CFRunLoopRun Exiting.");
                mDNS_Close(&mDNSStorage);
                }
-#endif __LIB_DISPATCH__
+#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 
        LogMsg("%s exiting", mDNSResponderVersionString);
 
@@ -2969,11 +2980,11 @@ mDNSexport mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback,
        newSource->kqs.KQcallback = callback;
        newSource->kqs.KQcontext  = context;
        newSource->kqs.KQtask     = "UDS client";
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
        newSource->kqs.readSource  = mDNSNULL;
        newSource->kqs.writeSource = mDNSNULL;
        newSource->kqs.fdClosed    = mDNSfalse;
-#endif __LIB_DISPATCH__
+#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 
        if (KQueueSet(fd, EV_ADD, EVFILT_READ, &newSource->kqs) == 0)
                {
index a1bd53053a0349b3fc6fb70c0f431ca94021f616..8cb7d5848630edb1120393691a424d552daab1be 100644 (file)
@@ -224,16 +224,22 @@ fin:
        (void)err;
        }
 
-void mDNSConfigureServer(int updown, const domainname *const fqdn)
+extern const char dnsprefix[];
+
+void mDNSConfigureServer(int updown, const char *const prefix, const domainname *const fqdn)
        {
        kern_return_t kr = KERN_SUCCESS;
        int retry = 0, err = 0;
-       char fqdnStr[MAX_ESCAPED_DOMAIN_NAME] = { 0 };
-       if (fqdn && ConvertDomainNameToCString(fqdn, fqdnStr))
+       char fqdnStr[MAX_ESCAPED_DOMAIN_NAME + 10] = { 0 }; // Assume the prefix is no larger than 10 chars
+       if (fqdn)
                {
-               // remove the trailing dot, as that is not used in the keychain entry racoon will lookup
-               mDNSu32 fqdnEnd = mDNSPlatformStrLen(fqdnStr);
-               if (fqdnEnd) fqdnStr[fqdnEnd - 1] = 0;
+               mDNSPlatformStrCopy(fqdnStr, prefix);
+               if (ConvertDomainNameToCString(fqdn, fqdnStr + mDNSPlatformStrLen(prefix)) && prefix == dnsprefix)
+                       {
+                       // remove the trailing dot, as that is not used in the keychain entry racoon will lookup
+                       mDNSu32 fqdnEnd = mDNSPlatformStrLen(fqdnStr);
+                       if (fqdnEnd) fqdnStr[fqdnEnd - 1] = 0;
+                       }
                }
        MACHRETRYLOOP_BEGIN(kr, retry, err, fin);
        kr = proxy_mDNSConfigureServer(getHelperPort(retry), updown, fqdnStr);
@@ -244,16 +250,20 @@ fin:
 
 int mDNSAutoTunnelSetKeys(int replacedelete, v6addr_t local_inner,
     v6addr_t local_outer, short local_port, v6addr_t remote_inner,
-    v6addr_t remote_outer, short remote_port, const domainname *const fqdn)
+    v6addr_t remote_outer, short remote_port, const char* const prefix, const domainname *const fqdn)
        {
        kern_return_t kr = KERN_SUCCESS;
        int retry = 0, err = 0;
-       char fqdnStr[MAX_ESCAPED_DOMAIN_NAME] = { 0 };
-       if (fqdn && ConvertDomainNameToCString(fqdn, fqdnStr))
+       char fqdnStr[MAX_ESCAPED_DOMAIN_NAME + 10] = { 0 }; // Assume the prefix is no larger than 10 chars
+       if (fqdn)
                {
-               // remove the trailing dot, as that is not used in the keychain entry racoon will lookup
-               mDNSu32 fqdnEnd = mDNSPlatformStrLen(fqdnStr);
-               if (fqdnEnd) fqdnStr[fqdnEnd - 1] = 0;
+               mDNSPlatformStrCopy(fqdnStr, prefix);
+               if (ConvertDomainNameToCString(fqdn, fqdnStr + mDNSPlatformStrLen(prefix)) && prefix == dnsprefix)
+                       {
+                       // remove the trailing dot, as that is not used in the keychain entry racoon will lookup
+                       mDNSu32 fqdnEnd = mDNSPlatformStrLen(fqdnStr);
+                       if (fqdnEnd) fqdnStr[fqdnEnd - 1] = 0;
+                       }
                }
        MACHRETRYLOOP_BEGIN(kr, retry, err, fin);
        kr = proxy_mDNSAutoTunnelSetKeys(getHelperPort(retry), replacedelete, local_inner, local_outer, local_port, remote_inner, remote_outer, remote_port, fqdnStr, &err);
@@ -272,3 +282,14 @@ void mDNSSendWakeupPacket(unsigned ifid, char *eth_addr, char *ip_addr, int iter
 fin:
        (void) err;
        }
+
+void mDNSPacketFilterControl(uint32_t command, char * ifname, uint16_t servicePort, uint16_t protocol)
+       {
+       kern_return_t kr = KERN_SUCCESS;
+       int retry = 0, err = 0;
+       MACHRETRYLOOP_BEGIN(kr, retry, err, fin);
+       kr = proxy_mDNSPacketFilterControl(getHelperPort(retry), command, ifname, servicePort, protocol);
+       MACHRETRYLOOP_END(kr, retry, err, fin);
+fin:
+       (void) err;
+       }
index a3a9046e5b0b853657ba696dce679b1bd5d227ba..73ceb3e70ee5b476a61c62afc9473d0281153be7 100644 (file)
@@ -57,6 +57,7 @@
 #include "helpermsgServer.h"
 #include "helper-server.h"
 #include "ipsec_options.h"
+#include "P2PPacketFilter.h"
 
 #ifndef RTF_IFSCOPE
 #define RTF_IFSCOPE 0x1000000
@@ -571,9 +572,13 @@ static void update_notification(void)
                // Note: the "\xEF\xBB\xBF" byte sequence in the CFS_Format string is the UTF-8 encoding of the zero-width non-breaking space character.
                // By appending this invisible character on the end of literal names, we ensure the these strings cannot inadvertently match any string
                // in the localization file -- since we know for sure that none of our strings in the localization file contain the ZWNBS character.
-               CFS_OQ               = CFStringCreateWithCString(NULL, "“",  kCFStringEncodingUTF8);
-               CFS_CQ               = CFStringCreateWithCString(NULL, "”",  kCFStringEncodingUTF8);
-               CFS_Format           = CFStringCreateWithCString(NULL, "%@%s\xEF\xBB\xBF", kCFStringEncodingUTF8);
+               //
+               // For languages that are written right to left, when we mix English (host names could be in english with brackets etc. and the
+               // rest in Arabic) we need unicode markups for proper formatting. The Unicode sequence 202C (UTF8 E2 80 AC), 200E (UTF8 E2 80 8E) and
+               // 202B (UTF8 E2 80 AB) helps with the formatting. See <rdar://problem/8629082> for more details.
+               CFS_OQ               = CFStringCreateWithCString(NULL, "“\xE2\x80\xAB",  kCFStringEncodingUTF8);
+               CFS_CQ               = CFStringCreateWithCString(NULL, "\xE2\x80\xAC”",  kCFStringEncodingUTF8);
+               CFS_Format           = CFStringCreateWithCString(NULL, "%@%s\xEF\xBB\xBF\xE2\x80\x8E", kCFStringEncodingUTF8);
                CFS_ComputerName     = CFStringCreateWithCString(NULL, "The name of your computer ",  kCFStringEncodingUTF8);
                CFS_ComputerNameMsg  = CFStringCreateWithCString(NULL, "To change the name of your computer, "
                        "open System Preferences and click Sharing, then type the name in the Computer Name field.",  kCFStringEncodingUTF8);
@@ -759,7 +764,7 @@ fin:
 
 enum DNSKeyFormat
        {
-       formatNotDNSKey, formatDdnsTypeItem, formatDnsPrefixedServiceItem
+       formatNotDNSKey, formatDdnsTypeItem, formatDnsPrefixedServiceItem, formatBtmmPrefixedServiceItem
        };
 
 // On Mac OS X on Intel, the four-character string seems to be stored backwards, at least sometimes.
@@ -770,14 +775,15 @@ enum DNSKeyFormat
 static const char dnsprefix[] = "dns:";
 static const char ddns[] = "ddns";
 static const char ddnsrev[] = "sndd";
+static const char btmmprefix[] = "btmmdns:";
 
 #ifndef NO_SECURITYFRAMEWORK
 static enum DNSKeyFormat
 getDNSKeyFormat(SecKeychainItemRef item, SecKeychainAttributeList **attributesp)
        {
-       static UInt32 tags[3] =
+       static UInt32 tags[4] =
                {
-               kSecTypeItemAttr, kSecServiceItemAttr, kSecAccountItemAttr
+               kSecTypeItemAttr, kSecServiceItemAttr, kSecAccountItemAttr, kSecLabelItemAttr
                };
        static SecKeychainAttributeInfo attributeInfo =
                {
@@ -808,6 +814,7 @@ getDNSKeyFormat(SecKeychainItemRef item, SecKeychainAttributeList **attributesp)
        "malformed result from SecKeychainItemCopyAttributesAndData - skipping");
                goto skip;
                }
+               
        debug("entry (\"%.*s\", \"%.*s\", \"%.*s\")",
            (int)attributes->attr[0].length, attributes->attr[0].data,
            (int)attributes->attr[1].length, attributes->attr[1].data,
@@ -829,6 +836,9 @@ getDNSKeyFormat(SecKeychainItemRef item, SecKeychainAttributeList **attributesp)
            0 == strncasecmp(attributes->attr[1].data, dnsprefix,
            sizeof(dnsprefix)-1))
                format = formatDnsPrefixedServiceItem;
+       else if (attributes->attr[1].length >= sizeof(btmmprefix)-1 &&
+           0 == strncasecmp(attributes->attr[1].data, btmmprefix, sizeof(btmmprefix)-1))
+               format = formatBtmmPrefixedServiceItem;
        else if (attributes->attr[0].length == sizeof(ddns)-1 &&
            0 == strncasecmp(attributes->attr[0].data, ddns, sizeof(ddns)-1))
                format = formatDdnsTypeItem;
@@ -849,6 +859,7 @@ skip:
        return formatNotDNSKey;
        }
 
+// Insert the attributes as defined by mDNSKeyChainAttributes
 static CFPropertyListRef
 getKeychainItemInfo(SecKeychainItemRef item,
     SecKeychainAttributeList *attributes, enum DNSKeyFormat format)
@@ -865,6 +876,8 @@ getKeychainItemInfo(SecKeychainItemRef item,
                debug("CFArrayCreateMutable failed");
                goto error;
                }
+
+       // Insert the Account attribute (kmDNSKcWhere)
        switch ((enum DNSKeyFormat)format)
        {
        case formatDdnsTypeItem:
@@ -872,9 +885,10 @@ getKeychainItemInfo(SecKeychainItemRef item,
                    attributes->attr[1].data, attributes->attr[1].length);
                break;
        case formatDnsPrefixedServiceItem:
+       case formatBtmmPrefixedServiceItem:
                data = CFDataCreate(kCFAllocatorDefault,
-                   attributes->attr[1].data + sizeof(dnsprefix)-1,
-                   attributes->attr[1].length - (sizeof(dnsprefix)-1));
+                   attributes->attr[1].data, attributes->attr[1].length);
+               break;
        default:
                assert("unknown DNSKeyFormat value");
                break;
@@ -886,6 +900,8 @@ getKeychainItemInfo(SecKeychainItemRef item,
                }
        CFArrayAppendValue(entry, data);
        CFRelease(data);
+
+       // Insert the Where attribute (kmDNSKcAccount)
        if (NULL == (data = CFDataCreate(kCFAllocatorDefault,
            attributes->attr[2].data, attributes->attr[2].length)))
                {
@@ -894,6 +910,8 @@ getKeychainItemInfo(SecKeychainItemRef item,
                }
        CFArrayAppendValue(entry, data);
        CFRelease(data);
+
+       // Insert the Key attribute (kmDNSKcKey)
        if (noErr != (status = SecKeychainItemCopyAttributesAndData(item, NULL,
            NULL, NULL, &keylen, &keyp)))
                {
@@ -911,6 +929,16 @@ getKeychainItemInfo(SecKeychainItemRef item,
                }
        CFArrayAppendValue(entry, data);
        CFRelease(data);
+
+       // Insert the Name attribute (kmDNSKcName)
+       if (NULL == (data = CFDataCreate(kCFAllocatorDefault,
+           attributes->attr[3].data, attributes->attr[3].length)))
+               {
+               debug("CFDataCreate for attr[3] failed");
+               goto error;
+               }
+       CFArrayAppendValue(entry, data);
+       CFRelease(data);
        return entry;
 
 error:
@@ -1408,7 +1436,7 @@ createAnonymousRacoonConfiguration(const char *fqdn)
          "  situation identity_only;\n"
          "  verify_identifier off;\n"
          "  generate_policy on;\n"
-         "  shared_secret keychain_by_id \"dns:";
+         "  shared_secret keychain_by_id \"";
        static const char config2[] =
          "\";\n"
          "  nonce_size 16;\n"
@@ -2146,7 +2174,7 @@ int
 do_mDNSAutoTunnelSetKeys(__unused mach_port_t port, int replacedelete,
     v6addr_t loc_inner, v6addr_t loc_outer6, uint16_t loc_port,
     v6addr_t rmt_inner, v6addr_t rmt_outer6, uint16_t rmt_port,
-    const char *fqdn, int *err, audit_token_t token)
+    const char *id, int *err, audit_token_t token)
        {
 #ifndef MDNS_NO_IPSEC
        static const char config[] =
@@ -2158,8 +2186,8 @@ do_mDNSAutoTunnelSetKeys(__unused mach_port_t port, int replacedelete,
          "  situation identity_only;\n"
          "  verify_identifier off;\n"
          "  generate_policy on;\n"
-         "  my_identifier user_fqdn \"dns:%s\";\n"
-         "  shared_secret keychain \"dns:%s\";\n"
+         "  my_identifier user_fqdn \"%s\";\n"
+         "  shared_secret keychain \"%s\";\n"
          "  nonce_size 16;\n"
          "  lifetime time 15 min;\n"
          "  initial_contact on;\n"
@@ -2294,7 +2322,7 @@ do_mDNSAutoTunnelSetKeys(__unused mach_port_t port, int replacedelete,
                        goto fin;
                        }
                fd = -1;
-               fprintf(fp, config, configHeader, (!rmt_port ? ro6 : ro), rmt_port, fqdn, fqdn, ri, li, li, ri);
+               fprintf(fp, config, configHeader, (!rmt_port ? ro6 : ro), rmt_port, id, id, ri, li, li, ri);
                fclose(fp);
                fp = NULL;
                if (0 > rename(tmp_path, path))
@@ -2343,7 +2371,7 @@ fin:
        unlink(tmp_path);
 #else
        (void)replacedelete; (void)loc_inner; (void)loc_outer6; (void)loc_port; (void)rmt_inner;
-       (void)rmt_outer6; (void)rmt_port; (void)fqdn; (void)token;
+       (void)rmt_outer6; (void)rmt_port; (void)id; (void)token;
        
        *err = kmDNSHelperIPsecDisabled;
 #endif /* MDNS_NO_IPSEC */
@@ -2439,3 +2467,44 @@ do_mDNSSendWakeupPacket(__unused mach_port_t port, unsigned ifid, const char *et
        close(bpf_fd);
        return KERN_SUCCESS;
        }
+
+// Open the specified port for protocol in the P2P firewall.
+kern_return_t
+do_mDNSPacketFilterControl(__unused mach_port_t port, uint32_t command, const char * ifname, uint16_t servicePort, uint16_t protocol, audit_token_t token)
+       {
+       (void) token; // unused
+       int     error;
+       kern_return_t result = KERN_SUCCESS;
+
+       helplog(ASL_LEVEL_INFO, "do_mDNSPacketFilterControl: command %d ifname %s, servicePort 0x%x, protocol %d", 
+                       command, ifname, servicePort, protocol);
+
+       switch (command)
+               {
+               case PF_SET_RULES:
+                       error = P2PPacketFilterAddBonjourRuleSet(ifname, servicePort, protocol);
+                       if (error)
+                               {
+                               helplog(ASL_LEVEL_ERR, "P2PPacketFilterAddBonjourRuleSet failed %s", strerror(error));
+                               result = KERN_FAILURE;
+                               }
+                       break;
+
+               case PF_CLEAR_RULES:
+                       error = P2PPacketFilterClearBonjourRules();
+                       if (error)
+                               {
+                               helplog(ASL_LEVEL_ERR, "P2PPacketFilterClearBonjourRules failed %s", strerror(error));
+                               result = KERN_FAILURE;
+                               }
+                       break;
+
+               default:
+                       helplog(ASL_LEVEL_ERR, "do_mDNSPacketFilterControl: invalid command %d", command);
+                       result = KERN_INVALID_ARGUMENT;
+                       break;
+               }
+
+       return result;
+       }
+
index e9abade30d2f8eae90e31eda1661fc8c8ddb9e12..dadf5d9e1bbbcaf7bf80fb71a0b919a87fca8db7 100644 (file)
@@ -47,6 +47,17 @@ enum mDNSAutoTunnelSetKeysReplaceDelete
        kmDNSAutoTunnelSetKeysDelete
        };
 
+// helper parses the system keychain and returns the information to mDNSResponder.
+// It returns four attributes. Attributes are defined after how they show up in
+// keychain access utility (the actual attribute name to retrieve these are different).
+enum mDNSKeyChainAttributes
+       {
+       kmDNSKcWhere,   // Where
+       kmDNSKcAccount, // Account
+       kmDNSKcKey,             // Key
+       kmDNSKcName             // Name
+       };
+
 #define ERROR(x, y) x,
 enum mDNSHelperErrors
        {
@@ -69,10 +80,11 @@ extern void mDNSDynamicStoreSetConfig(int key, const char *subkey, CFPropertyLis
 extern void mDNSPreferencesSetName(int key, domainlabel *old, domainlabel *new);
 extern int  mDNSKeychainGetSecrets(CFArrayRef *secrets);
 extern void mDNSAutoTunnelInterfaceUpDown(int updown, v6addr_t addr);
-extern void mDNSConfigureServer(int updown, const domainname *const fqdn);
+extern void mDNSConfigureServer(int updown, const char *const prefix, const domainname *const fqdn);
 extern int  mDNSAutoTunnelSetKeys(int replacedelete, v6addr_t local_inner,
                                v6addr_t local_outer, short local_port, v6addr_t remote_inner,
-                               v6addr_t remote_outer, short remote_port, const domainname *const fqdn);
+                               v6addr_t remote_outer, short remote_port, const char *const prefix, const domainname *const fqdn);
 extern void mDNSSendWakeupPacket(unsigned ifid, char *eth_addr, char *ip_addr, int iteration);
+extern void mDNSPacketFilterControl(uint32_t command, char * ifname, uint16_t servicePort, uint16_t protocol);
 
 #endif /* H_HELPER_H */
index 546621ef27c850ff604bf32fb8f52568e688acf4..50260787c5260b63d66e80a31f6f4347ba742233 100644 (file)
@@ -84,7 +84,7 @@ simpleroutine mDNSAutoTunnelInterfaceUpDown(
 simpleroutine mDNSConfigureServer(
                                                                port                    : mach_port_t;
                                                                updown                  : int;
-                                                               fqdn                    : string_t;
+                                                               id                              : string_t;
                ServerAuditToken                token                   : audit_token_t);
 
 routine mDNSAutoTunnelSetKeys( port                    : mach_port_t;
@@ -95,7 +95,7 @@ routine mDNSAutoTunnelSetKeys(        port                    : mach_port_t;
                                                                remote_inner    : v6addr_t;
                                                                remote_outer    : v6addr_t;
                                                                remote_port             : uint16_t;             /* Port expressed as a numeric integer value */
-                                                               fqdn                    : string_t;
+                                                               id                              : string_t;
                out                                             err                             : int;
                ServerAuditToken                token                   : audit_token_t);
 
@@ -106,3 +106,11 @@ simpleroutine mDNSSendWakeupPacket(
                                                                ip_addr                 : string_t;
                                                                iteration               : int;
                ServerAuditToken                token                   : audit_token_t);
+
+simpleroutine mDNSPacketFilterControl(
+                                                               port                    : mach_port_t;
+                                                               command                 : uint32_t;
+                                                               ifname                  : string_t;
+                                                               servicePort             : uint16_t;
+                                                               protocol                : uint16_t;
+               ServerAuditToken                token                   : audit_token_t);
index a3f3ad3105906e27111d8e0ad9d16aa76e89d1ca..dee00f3b4d84c06366765d36565e44178bde4497 100644 (file)
@@ -66,6 +66,7 @@
 #include <time.h>                   // platform support for UTC time
 #include <arpa/inet.h>              // for inet_aton
 #include <pthread.h>
+#include <netdb.h>                                     // for getaddrinfo
 
 #include <netinet/in.h>             // For IP_RECVTTL
 #ifndef IP_RECVTTL
 #include <IOKit/IOKitLib.h>
 #include <IOKit/IOMessage.h>
 
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 // This is currently defined in IOKit/PrivateHeaders/IOKitLibPrivate.h. Till it becomes an Public
 // API, we will have our own declaration
 void IONotificationPortSetDispatchQueue(IONotificationPortRef notify, dispatch_queue_t queue);
@@ -118,9 +119,12 @@ void IONotificationPortSetDispatchQueue(IONotificationPortRef notify, dispatch_q
 #include <mach/mach_port.h>
 #include <mach/mach_time.h>
 #include "helper.h"
+#include "P2PPacketFilter.h"
 
 #include <asl.h>
 
+#include <SystemConfiguration/SCDynamicStorePrivate.h>
+
 #if APPLE_OSX_mDNSResponder
 #include <DeviceToDeviceManager/DeviceToDeviceManager.h>
 #include <AWACS.h>
@@ -161,10 +165,11 @@ D2DStatus D2DRelease(D2DServiceInstance instanceHandle, D2DTransportType transpo
 // We currently do not offer sleep proxy service on laptops, or on machines that are set to go to sleep.
 mDNSexport int OfferSleepProxyService = 0;
 mDNSexport int DisableSleepProxyClient = 0;
+mDNSexport int UseInternalSleepProxy = 1;              // Set to non-zero to use internal (in-NIC) Sleep Proxy
 
 // We disable inbound relay connection if this value is set to true (typically via command-line switch).
 mDNSBool DisableInboundRelayConnection = mDNSfalse;
-mDNSexport int OSXVers;
+mDNSexport int OSXVers, iOSVers;
 mDNSexport int KQueueFD;
 
 #ifndef NO_SECURITYFRAMEWORK
@@ -188,7 +193,7 @@ static int   HINFO_HWstring_prefixlen = 6;
 
 mDNSexport int WatchDogReportingThreshold = 250;
 
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 dispatch_queue_t SSLqueue;
 #endif
 
@@ -201,6 +206,10 @@ mDNSexport int        ActiveDirectoryPrimaryDomainLabelCount;
 mDNSexport mDNSAddr   ActiveDirectoryPrimaryDomainServer;
 #endif // APPLE_OSX_mDNSResponder
 
+// Used by AutoTunnel
+const char btmmprefix[] = "btmmdns:";
+const char dnsprefix[] = "dns:";
+
 // ***************************************************************************
 #if COMPILER_LIKES_PRAGMA_MARK
 #pragma mark -
@@ -307,7 +316,7 @@ mDNSlocal mStatus DNSNameCompressionParseBytes(mDNS *const m, const mDNSu8 *cons
                { LogMsg("DNSNameCompressionParseBytes: failed to get large RR"); m->rec.r.resrec.RecordType = 0; return mStatus_UnknownErr; }
        else LogInfo("DNSNameCompressionParseBytes: got rr: %s", CRDisplayString(m, &m->rec.r));        
 
-       mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSInterface_P2P, m->rec.r.resrec.rrtype, 7200, kDNSRecordTypeShared, FreeD2DARElemCallback, NULL);
+       mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSInterface_P2P, m->rec.r.resrec.rrtype, 7200, kDNSRecordTypeShared, AuthRecordP2P, FreeD2DARElemCallback, NULL);
        AssignDomainName(&rr->namestorage, &m->rec.namestorage);
        rr->resrec.rdlength = m->rec.r.resrec.rdlength;
        rr->resrec.rdata->MaxRDLength = m->rec.r.resrec.rdlength;
@@ -478,7 +487,6 @@ mDNSlocal void xD2DAddToCache(mDNS *const m, D2DStatus result, D2DServiceInstanc
                        mDNSPlatformMemFree(ptr);
                        return;
                        }
-                       
                err = mDNS_Register(m, &ptr->ar);
                if (err)
                        {
@@ -698,6 +706,12 @@ mDNSexport void external_start_advertising_service(const ResourceRecord *const r
        DomainnameToLower(resourceRecord->name, &lower);
        
        LogInfo("external_start_advertising_service: %s", RRDisplayString(&mDNSStorage, resourceRecord));
+       // For SRV records, update packet filter if p2p interface already exists, otherwise,
+       // if will be updated when we get the KEV_DL_IF_ATTACHED event for the interface.
+       // Bonjour filter rules are removed when p2p interface KEV_DL_IF_DETACHED event is received.
+       if (resourceRecord->rrtype == kDNSType_SRV)
+               mDNSInitPacketFilter();
+
        if (resourceRecord->rrtype == kDNSServiceType_A || resourceRecord->rrtype == kDNSServiceType_AAAA)
                {
                LogInfo("external_start_advertising_service: ignoring address record");
@@ -885,7 +899,7 @@ mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(mDNS *const
        }
 
 
-mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id)
+mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id, mDNSBool suppressNetworkChange)
        {
        NetworkInterfaceInfoOSX *i;
        if (id == mDNSInterface_LocalOnly) return(kDNSServiceInterfaceIndexLocalOnly);
@@ -898,6 +912,9 @@ mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNS
        for (i = m->p->InterfaceList; i; i = i->next)
                if (i->scope_id == scope_id) return(i->scope_id);
 
+       // If we are supposed to suppress network change, return "id" back 
+       if (suppressNetworkChange) return scope_id;
+
        // Not found. Make sure our interface list is up to date, then try again.
        LogInfo("Interface index for InterfaceID %p not found; Updating interface list", id);
        mDNSMacOSXNetworkChanged(m);
@@ -910,7 +927,7 @@ mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNS
 #if APPLE_OSX_mDNSResponder
 mDNSexport void mDNSASLLog(uuid_t *uuid, const char *subdomain, const char *result, const char *signature, const char *fmt, ...)
        {
-       if (OSXVers < OSXVers_10_6_SnowLeopard)         return;
+       if (OSXVers < OSXVers_10_6_SnowLeopard) return; // Only do ASL on Mac OS X 10.6 and later (not on iOS)
 
        static char             buffer[512];
        aslmsg                  asl_msg = asl_new(ASL_TYPE_MSG);
@@ -1054,7 +1071,14 @@ mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const ms
                if (info && mDNSAddrIsDNSMulticast(dst))        // Specify outgoing interface
                        {
                        err = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &info->scope_id, sizeof(info->scope_id));
-                       if (err < 0) LogMsg("setsockopt - IPV6_MULTICAST_IF error %d errno %d (%s)", err, errno, strerror(errno));
+                       if (err < 0)
+                               {
+                               char name[IFNAMSIZ];
+                               if (if_indextoname(info->scope_id, name) != NULL)
+                                       LogMsg("setsockopt - IPV6_MULTICAST_IF error %d errno %d (%s)", err, errno, strerror(errno));
+                               else
+                                       LogInfo("setsockopt - IPV6_MUTLICAST_IF scopeid %d, not a valid interface", info->scope_id);
+                               }
                        }
                }
 #endif
@@ -1417,13 +1441,13 @@ mDNSlocal OSStatus tlsSetupSock(TCPSocket *sock, mDNSBool server)
        return(err);
        }
 
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 mDNSlocal void doSSLHandshake(void *ctx)
        {
        TCPSocket *sock = (TCPSocket*)ctx;
        mStatus err = SSLHandshake(sock->tlsContext);
        
-       //Can't have multiple threads in mDNS core. When __LIB_DISPATCH__ is
+       //Can't have multiple threads in mDNS core. When MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM is
        //defined, KQueueLock is a noop. Hence we need to serialize here
        //
        //NOTE: We just can't serialize doTcpSocketCallback alone on the main queue.
@@ -1527,7 +1551,7 @@ mDNSlocal mStatus spawnSSLHandshake(TCPSocket* sock)
        if (sock->handshake != handshake_required) LogMsg("spawnSSLHandshake: handshake status not required: %d", sock->handshake);
        sock->handshake = handshake_in_progress;
        KQueueSet(sock->fd, EV_DELETE, EVFILT_READ, sock->kqEntry);
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 
        // Dispatch it on a separate serial queue to avoid deadlocks with threads running on main queue
        dispatch_async(SSLqueue, ^{doSSLHandshake(sock);});
@@ -1592,7 +1616,7 @@ mDNSlocal void tcpKQSocketCallback(__unused int fd, short filter, void *context)
        doTcpSocketCallback(sock);
        }
 
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 mDNSexport int KQueueSet(int fd, u_short flags, short filter, KQueueEntry *const entryRef)
        {
        dispatch_queue_t queue = dispatch_get_main_queue();
@@ -1703,7 +1727,7 @@ mDNSexport void KQueueUnlock(mDNS *const m, const char const *task)
 
 mDNSexport void mDNSPlatformCloseFD(KQueueEntry *kq, int fd)
        {
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
        (void)fd; //unused
        if (kq->readSource)
                {
@@ -1771,8 +1795,8 @@ mDNSlocal mStatus SetupTCPSocket(TCPSocket *sock, u_short sa_family, mDNSIPPort
                if (err < 0) { LogMsg("ERROR: bind6 %s", strerror(errno)); return err; }
 
                // We want to receive destination addresses and receive interface identifiers
-        err = setsockopt(skt, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on));
-               if (err < 0) { LogMsg("ERROR: setsockopt IPV6_PKTINFO %s", strerror(errno)); return err; }
+        err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
+               if (err < 0) { LogMsg("ERROR: setsockopt IPV6_RECVPKTINFO %s", strerror(errno)); return err; }
 
                mDNSPlatformMemZero(&addr6, sizeof(addr6));
                socklen_t len = sizeof(addr6);
@@ -1786,7 +1810,7 @@ mDNSlocal mStatus SetupTCPSocket(TCPSocket *sock, u_short sa_family, mDNSIPPort
        k->KQcallback = tcpKQSocketCallback;
        k->KQcontext  = sock;
        k->KQtask     = "mDNSPlatformTCPSocket";
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
        k->readSource = mDNSNULL;
        k->writeSource = mDNSNULL;
        k->fdClosed = mDNSfalse;
@@ -2011,10 +2035,11 @@ mDNSexport void mDNSPlatformTCPCloseConnection(TCPSocket *sock)
                        if (sock->handshake == handshake_in_progress) // SSLHandshake thread using this sock (esp. tlsContext)
                                {
                                LogInfo("mDNSPlatformTCPCloseConnection: called while handshake in progress");
+                               // When we come back from SSLHandshake, we will notice that a close was here and
+                               // call this function again which will do the cleanup then.
                                sock->handshake = handshake_to_be_closed;
-                               }
-                       if (sock->handshake == handshake_to_be_closed)
                                return;
+                               }
 
                        SSLClose(sock->tlsContext);
                        SSLDisposeContext(sock->tlsContext);
@@ -2186,12 +2211,12 @@ mDNSlocal mStatus SetupSocket(KQSocketSet *cp, const mDNSIPPort port, u_short sa
                if (mDNSSameIPPort(port, NATPMPAnnouncementPort)) { if (outport) *outport = zeroIPPort; return mStatus_NoError; }
                
                // We want to receive destination addresses and receive interface identifiers
-               err = setsockopt(skt, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on));
-               if (err < 0) { errstr = "setsockopt - IPV6_PKTINFO"; goto fail; }
+               err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
+               if (err < 0) { errstr = "setsockopt - IPV6_RECVPKTINFO"; goto fail; }
 
                // We want to receive packet hop count value so we can check it
-               err = setsockopt(skt, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, sizeof(on));
-               if (err < 0) { errstr = "setsockopt - IPV6_HOPLIMIT"; goto fail; }
+               err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on));
+               if (err < 0) { errstr = "setsockopt - IPV6_RECVHOPLIMIT"; goto fail; }
 
                // We want to receive only IPv6 packets. Without this option we get IPv4 packets too,
                // with mapped addresses of the form 0:0:0:0:0:FFFF:xxxx:xxxx, where xxxx:xxxx is the IPv4 address
@@ -2231,7 +2256,7 @@ mDNSlocal mStatus SetupSocket(KQSocketSet *cp, const mDNSIPPort port, u_short sa
        k->KQcallback = myKQSocketCallBack;
        k->KQcontext  = cp;
        k->KQtask     = "UDP packet reception";
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
        k->readSource = mDNSNULL;
        k->writeSource = mDNSNULL;
        k->fdClosed = mDNSfalse;
@@ -2363,7 +2388,7 @@ mDNSexport void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSA
 mDNSlocal void CloseBPF(NetworkInterfaceInfoOSX *const i)
        {
        LogSPS("%s closing BPF fd %d", i->ifinfo.ifname, i->BPF_fd);
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
        // close will happen in the cancel handler
        dispatch_source_cancel(i->BPF_source);
 #else
@@ -2415,7 +2440,7 @@ mDNSlocal void bpf_callback_common(NetworkInterfaceInfoOSX *info)
 exit:
        KQueueUnlock(info->m, "bpf_callback");
        }
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 mDNSlocal void bpf_callback_dispatch(NetworkInterfaceInfoOSX *const info)
        {
        bpf_callback_common(info);
@@ -2647,7 +2672,8 @@ mDNSexport void mDNSPlatformReceiveBPF_fd(mDNS *const m, int fd)
                        i->BPF_len = sizeof(m->imsg);
                        if (ioctl(fd, BIOCSBLEN, &i->BPF_len) < 0)
                                LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCSBLEN failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno));
-                       else LogSPS("mDNSPlatformReceiveBPF_fd: %d %s BIOCSBLEN %d", i->BPF_len);
+                       else 
+                               LogSPS("mDNSPlatformReceiveBPF_fd: %d %s BIOCSBLEN %d", fd, i->ifinfo.ifname, i->BPF_len);
                        }
        
                static const u_int opt_one = 1;
@@ -2667,7 +2693,7 @@ mDNSexport void mDNSPlatformReceiveBPF_fd(mDNS *const m, int fd)
                        { LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCSETIF failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); i->BPF_fd = -3; }
                else
                        {
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
                        i->BPF_fd  = fd;
                        i->BPF_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, dispatch_get_main_queue());
                        if (!i->BPF_source) {LogMsg("mDNSPlatformReceiveBPF_fd: dispatch source create failed");return;}
@@ -2966,7 +2992,13 @@ mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifad
                        mDNSSameAddress(&ip, &(*p)->ifinfo.ip) &&
                        mDNSSameEthAddress(&bssid, &(*p)->BSSID))
                        {
-                       debugf("AddInterfaceToList: Found existing interface %lu %.6a with address %#a at %p", scope_id, &bssid, &ip, *p);
+                       debugf("AddInterfaceToList: Found existing interface %lu %.6a with address %#a at %p, ifname before %s, after %s", scope_id, &bssid, &ip, *p, (*p)->ifinfo.ifname, ifa->ifa_name);
+                       // The name should be updated to the new name so that we don't report a wrong name in our SIGINFO output.
+                       // When interfaces are created with same MAC address, kernel resurrects the old interface.
+                       // Even though the interface index is the same (which should be sufficient), when we receive a UDP packet
+                       // we get the corresponding name for the interface index on which the packet was received and check against
+                       // the InterfaceList for a matching name. So, keep the name in sync
+                       strlcpy((*p)->ifinfo.ifname, ifa->ifa_name, sizeof((*p)->ifinfo.ifname));
                        (*p)->Exists = mDNStrue;
                        // If interface was not in getifaddrs list last time we looked, but it is now, update 'AppearanceTime' for this record
                        if ((*p)->LastSeen != utc) (*p)->AppearanceTime = utc;
@@ -3006,6 +3038,7 @@ mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifad
        // local-only services, which need a loopback address record.
        i->ifinfo.Advertise   = m->DivertMulticastAdvertisements ? ((ifa->ifa_flags & IFF_LOOPBACK) ? mDNStrue : mDNSfalse) : m->AdvertiseLocalAddresses;
        i->ifinfo.McastTxRx   = mDNSfalse; // For now; will be set up later at the end of UpdateInterfaceList
+       i->ifinfo.Loopback    = ((ifa->ifa_flags & IFF_LOOPBACK) != 0) ? mDNStrue : mDNSfalse;
 
        i->next            = mDNSNULL;
        i->m               = m;
@@ -3064,13 +3097,15 @@ static CFMutableDictionaryRef domainStatusDict = NULL;
 mDNSlocal void RemoveAutoTunnelDomainStatus(const mDNS *const m, const DomainAuthInfo *const info)
        {
        char buffer[1024];
+       mDNSu32 buflen;
        CFStringRef domain;
 
        LogInfo("RemoveAutoTunnelDomainStatus: %##s", info->domain.c);
 
        if (!domainStatusDict) { LogMsg("RemoveAutoTunnelDomainStatus: No domainStatusDict"); return; }
        
-       buffer[mDNS_snprintf(buffer, sizeof(buffer), "%##s", info->domain.c) - 1] = 0;
+       buflen = mDNS_snprintf(buffer, sizeof(buffer), "%##s", info->domain.c);
+       if (info->AutoTunnel == dnsprefix) buffer[buflen-1] = 0; // Strip the trailing dot for Classic
        domain = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8);
        if (!domain) { LogMsg("RemoveAutoTunnelDomainStatus: Could not create CFString domain"); return; }
        
@@ -3159,11 +3194,14 @@ mDNSlocal void UpdateAutoTunnelDomainStatus(const mDNS *const m, const DomainAut
        const NATTraversalInfo *const llq = m->LLQNAT.clientContext ? &m->LLQNAT : mDNSNULL;
        const NATTraversalInfo *const tun = info->AutoTunnelNAT.clientContext ? &info->AutoTunnelNAT : mDNSNULL;
        char buffer[1024];
+       mDNSu32 buflen = 0;
        CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
        CFStringRef domain = NULL;
        CFStringRef tmp = NULL;
        CFNumberRef num = NULL;
        mStatus status = mStatus_NoError;
+       mStatus llqStatus = mStatus_NoError;
+       char llqBuffer[1024];
        
        if (!m->mDNS_busy) LogMsg("UpdateAutoTunnelDomainStatus: ERROR!! Lock not held");
        if (!domainStatusDict)
@@ -3174,7 +3212,8 @@ mDNSlocal void UpdateAutoTunnelDomainStatus(const mDNS *const m, const DomainAut
        
        if (!dict) { LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFDictionary dict"); return; }
 
-       buffer[mDNS_snprintf(buffer, sizeof(buffer), "%##s", info->domain.c) - 1] = 0;
+       buflen = mDNS_snprintf(buffer, sizeof(buffer), "%##s", info->domain.c);
+       if (info->AutoTunnel == dnsprefix) buffer[buflen-1] = 0; // Strip the trailing dot for Classic
        domain = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8);
        if (!domain) { LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString domain"); return; }
 
@@ -3263,73 +3302,71 @@ mDNSlocal void UpdateAutoTunnelDomainStatus(const mDNS *const m, const DomainAut
                        }
                }
        
-       // If we have a relay address which lets other hosts to reach us through the relay, then we should
-       // report success except for the LLQs they don't go over the relay connection. If LLQs fail, then
-       // report failure. In future, when LLQs go over the relay connection, we don't need this logic
-       if (!mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddrIn))
-               {
-               // If we have a bad signature error updating RR, it overrides any error as
-               // the user needs to be notified immediately 
-               status = UpdateRRStatus(m, buffer, sizeof(buffer), info);
-               if (status == mStatus_NoError)  
-                       {
-                       status = UpdateLLQStatus(m, buffer, sizeof(buffer), info);
-                       if (status == mStatus_PollingMode)      
-                               {
-                               // If we have a relay connection and we are in polling mode, report as success.
-                               // This normally happens when we are behind Double NAT or NAT with UPnP/NAT-PMP
-                               // disabled but we are able to successfully file share/screen share with the help
-                               // of the relay connection. As it just affects the discovery/update of the other
-                               // BTMM hosts, we consider it as minor issue and report it as success.
-                               LogInfo("UpdateAutoTunnelDomainStatus:NonzeroRelayAddress: Polling reported as success");
-                               status = mStatus_NoError;
-                               mDNS_snprintf(buffer, sizeof(buffer), "Success");
-                               }
-                       }
-               LogInfo("UpdateAutoTunnelDomainStatus:NonzeroRelayAddress: Status %d, %s", status, buffer);
+       mDNS_snprintf(buffer, sizeof(buffer), "Success");
+       llqStatus = UpdateLLQStatus(m, llqBuffer, sizeof(llqBuffer), info);
+       status = UpdateRRStatus(m, buffer, sizeof(buffer), info);
+
+       // If we have a bad signature error updating a RR, it overrides any error as it needs to be
+       // reported so that it can be fixed automatically (or the user needs to be notified)
+       if (status != mStatus_NoError)
+               {
+               LogInfo("UpdateAutoTunnelDomainStatus: RR Status %d, %s", status, buffer);
+               }
+       else if (m->Router.type == mDNSAddrType_None)
+               {
+               status = mStatus_NoRouter;
+               mDNS_snprintf(buffer, sizeof(buffer), "No network connection - none");
+               }
+       else if (m->Router.type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsZero(m->Router.ip.v4))
+               {
+               status = mStatus_NoRouter;
+               mDNS_snprintf(buffer, sizeof(buffer), "No network connection - v4 zero");
+               }
+       else if (mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddrOut))
+               {
+               status = info->AutoTunnel == btmmprefix ? mStatus_ServiceNotRunning : mStatus_PollingMode;
+               mDNS_snprintf(buffer, sizeof(buffer), "No relay connection");
                }
        else if (!llq && !tun)
                {
                status = mStatus_NotInitializedErr;
                mDNS_snprintf(buffer, sizeof(buffer), "Neither LLQ nor AutoTunnel NAT port mapping is currently active");
                }
-       else if ((llq && llq->Result == mStatus_DoubleNAT) || (tun && tun->Result == mStatus_DoubleNAT))
+       else if (llqStatus == mStatus_NoSuchRecord)
+               {
+               status = llqStatus;
+               mDNS_snprintf(buffer, sizeof(buffer), llqBuffer);
+               }
+       else if (info->AutoTunnel == btmmprefix && ((llq && llq->Result == mStatus_DoubleNAT) || (tun && tun->Result == mStatus_DoubleNAT)))
                {
                status = mStatus_DoubleNAT;
                mDNS_snprintf(buffer, sizeof(buffer), "Double NAT: Router is reporting an external address");
                }
-       else if ((llq && llq->Result == mStatus_NATPortMappingDisabled) || (tun && tun->Result == mStatus_NATPortMappingDisabled) ||
-                (m->LastNATMapResultCode == NATErr_Refused && ((llq && !llq->Result && mDNSIPPortIsZero(llq->ExternalPort)) || (tun && !tun->Result && mDNSIPPortIsZero(tun->ExternalPort)))))
+       else if (info->AutoTunnel == btmmprefix && ((llq && llq->Result == mStatus_NATPortMappingDisabled) || (tun && tun->Result == mStatus_NATPortMappingDisabled) ||
+                (m->LastNATMapResultCode == NATErr_Refused && ((llq && !llq->Result && mDNSIPPortIsZero(llq->ExternalPort)) || (tun && !tun->Result && mDNSIPPortIsZero(tun->ExternalPort))))))
                {
                status = mStatus_NATPortMappingDisabled;
                mDNS_snprintf(buffer, sizeof(buffer), "NAT-PMP is disabled on the router");
                }
-       else if ((llq && llq->Result) || (tun && tun->Result))
+       else if (info->AutoTunnel == btmmprefix && ((llq && llq->Result) || (tun && tun->Result)))
                {
                status = mStatus_NATTraversal;
                mDNS_snprintf(buffer, sizeof(buffer), "Error obtaining NAT port mapping from router");
                }
-       else if (m->Router.type == mDNSAddrType_None)
-               {
-               status = mStatus_NoRouter;
-               mDNS_snprintf(buffer, sizeof(buffer), "No network connection - none");
-               }
-       else if (m->Router.type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsZero(m->Router.ip.v4))
-               {
-               status = mStatus_NoRouter;
-               mDNS_snprintf(buffer, sizeof(buffer), "No network connection - v4 zero");
-               }
-       else if ((llq && mDNSIPPortIsZero(llq->ExternalPort)) || (tun && mDNSIPPortIsZero(tun->ExternalPort)))
+       else if (info->AutoTunnel == btmmprefix && ((llq && mDNSIPPortIsZero(llq->ExternalPort)) || (tun && mDNSIPPortIsZero(tun->ExternalPort))))
                {
                status = mStatus_NATTraversal;
                mDNS_snprintf(buffer, sizeof(buffer), "Unable to obtain NAT port mapping from router");
                }
+       else if (info->AutoTunnel == btmmprefix || llqStatus != mStatus_PollingMode)
+               {
+               status = llqStatus;
+               mDNS_snprintf(buffer, sizeof(buffer), llqBuffer);               
+               LogInfo("UpdateAutoTunnelDomainStatus: LLQ Status %d, %s", status, buffer);
+               }
        else
                {
-               status = UpdateRRStatus(m, buffer, sizeof(buffer), info);
-               if (status == mStatus_NoError)  
-                       status = UpdateLLQStatus(m, buffer, sizeof(buffer), info);
-               LogInfo("UpdateAutoTunnelDomainStatus:ZeroRelayAddress: Status %d, %s", status, buffer);
+               mDNS_snprintf(buffer, sizeof(buffer), "Polling success");               
                }
        
        num = CFNumberCreate(NULL, kCFNumberSInt32Type, &status);
@@ -3420,7 +3457,7 @@ mDNSlocal void UpdateAnonymousRacoonConfig(mDNS *m)               // Determine whether we nee
                {
                AnonymousRacoonConfig = info;
                // Create or revert configuration file, and start (or SIGHUP) Racoon
-               (void)mDNSConfigureServer(AnonymousRacoonConfig ? kmDNSUp : kmDNSDown, AnonymousRacoonConfig ? &AnonymousRacoonConfig->domain : mDNSNULL);
+               (void)mDNSConfigureServer(AnonymousRacoonConfig ? kmDNSUp : kmDNSDown, AnonymousRacoonConfig ? AnonymousRacoonConfig->AutoTunnel : mDNSNULL, AnonymousRacoonConfig ? &AnonymousRacoonConfig->domain : mDNSNULL);
                }
        }
 
@@ -3683,7 +3720,6 @@ mDNSlocal void RegisterAutoTunnel6Record(mDNS *m, DomainAuthInfo *info)
                return;
                }
 
-       // if disabled administratively, don't register
        // if disabled administratively, don't register
        if (!m->RegisterAutoTunnel6 || DisableInboundRelayConnection)
                {
@@ -3859,7 +3895,9 @@ mDNSlocal void AutoTunnelHostNameChanged(mDNS *m, DomainAuthInfo *info)
        {
        LogInfo("AutoTunnelHostNameChanged %#s.%##s", m->hostlabel.c, info->domain.c);
 
+#ifndef NO_SECURITYFRAMEWORK
        DeregisterAutoTunnelDevInfoRecord(m, info);
+#endif
        DeregisterAutoTunnelServiceRecords(m, info);
        DeregisterAutoTunnel6Record(m, info);
        RegisterAutoTunnelServiceRecords(m, info);
@@ -3898,12 +3936,12 @@ mDNSlocal void SetupLocalAutoTunnel6Records(mDNS *const m, DomainAuthInfo *info)
 
        if (info->AutoTunnelDeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered)
                {
-               mDNS_SetupResourceRecord(&info->AutoTunnelDeviceInfo, mDNSNULL, mDNSInterface_Any, kDNSType_TXT,  kStandardTTL, kDNSRecordTypeUnregistered, AutoTunnelRecordCallback, info);
+               mDNS_SetupResourceRecord(&info->AutoTunnelDeviceInfo, mDNSNULL, mDNSInterface_Any, kDNSType_TXT,  kStandardTTL, kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info);
                RegisterAutoTunnelDevInfoRecord(m, info);
                }
        if (info->AutoTunnel6Record.resrec.RecordType == kDNSRecordTypeUnregistered)
                {
-               mDNS_SetupResourceRecord(&info->AutoTunnel6Record, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL, kDNSRecordTypeUnregistered, AutoTunnelRecordCallback, info);
+               mDNS_SetupResourceRecord(&info->AutoTunnel6Record, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL, kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info);
                RegisterAutoTunnel6Record(m, info);
                }
 
@@ -3948,11 +3986,11 @@ mDNSexport void SetupLocalAutoTunnelInterface_internal(mDNS *const m, mDNSBool s
                                AbortDeregistration(m, &info->AutoTunnelHostRecord);
 
                                mDNS_SetupResourceRecord(&info->AutoTunnelTarget,     mDNSNULL, mDNSInterface_Any, kDNSType_A,    kHostNameTTL,
-                                       kDNSRecordTypeUnregistered, AutoTunnelRecordCallback, info);
+                                       kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info);
                                mDNS_SetupResourceRecord(&info->AutoTunnelService,    mDNSNULL, mDNSInterface_Any, kDNSType_SRV,  kHostNameTTL,
-                                       kDNSRecordTypeUnregistered, AutoTunnelRecordCallback, info);
+                                       kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info);
                                mDNS_SetupResourceRecord(&info->AutoTunnelHostRecord, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL,
-                                       kDNSRecordTypeUnregistered, AutoTunnelRecordCallback, info);
+                                       kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info);
 
                                // Try to get a NAT port mapping for the AutoTunnelService
                                info->AutoTunnelNAT.clientCallback   = AutoTunnelNATCallback;
@@ -4003,7 +4041,7 @@ mDNSlocal mStatus AutoTunnelSetKeys(ClientTunnel *tun, mDNSBool AddNew)
                rmt_outer6.b[3] = tun->rmt_outer.b[3];
                }
 
-       return(mDNSAutoTunnelSetKeys(AddNew ? kmDNSAutoTunnelSetKeysReplace : kmDNSAutoTunnelSetKeysDelete, tun->loc_inner.b, loc_outer6.b, kRacoonPort, tun->rmt_inner.b, rmt_outer6.b, mDNSVal16(tun->rmt_outer_port), SkipLeadingLabels(&tun->dstname, 1)));
+       return(mDNSAutoTunnelSetKeys(AddNew ? kmDNSAutoTunnelSetKeysReplace : kmDNSAutoTunnelSetKeysDelete, tun->loc_inner.b, loc_outer6.b, kRacoonPort, tun->rmt_inner.b, rmt_outer6.b, mDNSVal16(tun->rmt_outer_port), tun->prefix, SkipLeadingLabels(&tun->dstname, 1)));
        }
 
 // If the EUI-64 part of the IPv6 ULA matches, then that means the two addresses point to the same machine
@@ -4329,6 +4367,7 @@ mDNSexport void AddNewClientTunnel(mDNS *const m, DNSQuestion *const q)
        {
        ClientTunnel *p = mallocL("ClientTunnel", sizeof(ClientTunnel));
        if (!p) return;
+       p->prefix = q->AuthInfo->AutoTunnel;
        AssignDomainName(&p->dstname, &q->qname);
        p->MarkedForDeletion = mDNSfalse;
        p->loc_inner      = zerov6Addr;
@@ -4352,7 +4391,12 @@ mDNSexport void AddNewClientTunnel(mDNS *const m, DNSQuestion *const q)
        p->q.ForceMCast       = mDNSfalse;
        p->q.ReturnIntermed   = mDNStrue;
        p->q.SuppressUnusable = mDNSfalse;
-       p->q.WakeOnResolve    = mDNSfalse;
+       p->q.SearchListIndex  = 0;
+       p->q.AppendSearchDomains = 0;
+       p->q.RetryWithSearchDomains = mDNSfalse;
+       p->q.TimeoutQuestion  = 0;
+       p->q.WakeOnResolve    = 0;
+       p->q.qnameOrig        = mDNSNULL;
        p->q.QuestionCallback = AutoTunnelCallback;
        p->q.QuestionContext  = p;
 
@@ -4379,6 +4423,12 @@ mDNSlocal mStatus UpdateInterfaceList(mDNS *const m, mDNSs32 utc)
        int InfoSocket              = socket(AF_INET6, SOCK_DGRAM, 0);
        if (InfoSocket < 3 && errno != EAFNOSUPPORT) LogMsg("UpdateInterfaceList: InfoSocket error %d errno %d (%s)", InfoSocket, errno, strerror(errno));
 #endif
+
+       // During wakeup, we may get a network change notification e.g., new addresses, before we get
+       // a wake notification. This means that we have not set AnnounceOwner. Registering interfaces with
+       // core would cause us to probe again which will conflict with the sleep proxy server, if we had
+       // registered with it when going to sleep. Hence, need to delay until we get the wake notification
+
        if (m->SleepState == SleepState_Sleeping) ifa = NULL;
 
        while (ifa)
@@ -4638,7 +4688,22 @@ mDNSlocal int SetupActiveInterfaces(mDNS *const m, mDNSs32 utc)
                                // If the interface is an old one that went away and came back in less than a minute, then we're in a flapping scenario.
                                i->Occulting = !(i->ifa_flags & IFF_LOOPBACK) && (utc - i->LastSeen > 0 && utc - i->LastSeen < 60);
 
-                               mDNS_RegisterInterface(m, n, i->Flashing && i->Occulting);
+                               // Temporary fix to handle P2P flapping. P2P reuses the scope-id, mac address and the IP address
+                               // everytime it creates a new interface. We think it is a duplicate and hence consider it
+                               // as flashing and occulting, that is, flapping. If an interface is marked as flapping, 
+                               // mDNS_RegisterInterface() changes the probe delay from 1/2 second to 5 seconds and
+                               // logs a warning message to system.log noting frequent interface transitions.
+                               if (strncmp(i->ifinfo.ifname, "p2p", 3) == 0)
+                                       {
+                                       LogInfo("SetupActiveInterfaces: P2P %s interface registering %s %s", i->ifinfo.ifname,
+                                               i->Flashing               ? " (Flashing)"  : "",
+                                               i->Occulting              ? " (Occulting)" : "");
+                                       mDNS_RegisterInterface(m, n, 0);
+                                       }
+                               else
+                                       {
+                                       mDNS_RegisterInterface(m, n, i->Flashing && i->Occulting);
+                                       }
 
                                if (!mDNSAddressIsLinkLocal(&n->ip)) count++;
                                LogInfo("SetupActiveInterfaces:   Registered    %5s(%lu) %.6a InterfaceID %p(%p), primary %p, %#a/%d%s%s%s",
@@ -4745,7 +4810,23 @@ mDNSlocal int ClearInactiveInterfaces(mDNS *const m, mDNSs32 utc)
                                        i->Flashing               ? " (Flashing)"  : "",
                                        i->Occulting              ? " (Occulting)" : "",
                                        i->ifinfo.InterfaceActive ? " (Primary)"   : "");
-                               mDNS_DeregisterInterface(m, &i->ifinfo, i->Flashing && i->Occulting);
+
+                               // Temporary fix to handle P2P flapping. P2P reuses the scope-id, mac address and the IP address
+                               // everytime it creates a new interface. We think it is a duplicate and hence consider it
+                               // as flashing and occulting. The "core" does not flush the cache for this case. This leads to
+                               // stale data returned to the application even after the interface is removed. The application
+                               // then starts to send data but the new interface is not yet created.
+                               if (strncmp(i->ifinfo.ifname, "p2p", 3) == 0)
+                                       {
+                                       LogInfo("ClearInactiveInterfaces: P2P %s interface deregistering %s %s", i->ifinfo.ifname,
+                                               i->Flashing               ? " (Flashing)"  : "",
+                                               i->Occulting              ? " (Occulting)" : "");
+                                       mDNS_DeregisterInterface(m, &i->ifinfo, 0);
+                                       }
+                               else
+                                       {
+                                       mDNS_DeregisterInterface(m, &i->ifinfo, i->Flashing && i->Occulting);
+                                       }
                                if (!mDNSAddressIsLinkLocal(&i->ifinfo.ip)) count++;
                                i->Registered = mDNSNULL;
                                // Note: If i->Registered is set, that means we've called mDNS_RegisterInterface() for this interface,
@@ -4809,9 +4890,18 @@ mDNSlocal int compare_dns_configs(const void *aa, const void *bb)
        return (a->search_order < b->search_order) ? -1 : (a->search_order == b->search_order) ? 0 : 1;
        }
 
+// ConfigResolvers is called twice - once to parse the "scoped_resolver" list and second time to parse the "resolver" list.
+// "scoped_resolver" has entries that should only be used for "scoped_questions" (for questions that specify an interface index
+// q->InterfaceID) and "resolver" entries should only be used for non-scoped questions. Entries in either of the list can specify
+// an ifindex. This means that the dns query should be scoped to that interface when sent out on the wire. The flag value
+// "DNS_RESOLVER_FLAGS_SCOPED" itself appears only in "scoped" list of resolvers.
+//
+// Before "scoped_resolver" was introduced, the entries in "resolver" list can contain options like "interface=en0" which
+// was meant to scope the query (non-scoped queries) to a specific interface. We still support this option. On top of that,
+// we also support a new way of specifying the interface index as described above.
 mDNSlocal void ConfigResolvers(mDNS *const m, dns_config_t *config, mDNSBool scope, mDNSBool setsearch, mDNSBool setservers)
        {
-       int i;
+       int i, j;
        domainname d;
 #if DNSINFO_VERSION >= 20091104
        dns_resolver_t **resolver = scope ? config->scoped_resolver : config->resolver;
@@ -4822,7 +4912,6 @@ mDNSlocal void ConfigResolvers(mDNS *const m, dns_config_t *config, mDNSBool sco
        int nresolvers = config->n_resolver;
 #endif
 
-       // Currently we don't support search lists for scoped resolvers. The WAB support for this will be added later.
        if (setsearch && !scope && nresolvers)
                {
                // Due to the vagaries of Apple's SystemConfiguration and dnsinfo.h APIs, if there are no search domains
@@ -4839,19 +4928,20 @@ mDNSlocal void ConfigResolvers(mDNS *const m, dns_config_t *config, mDNSBool sco
                if (resolver[0]->n_search == 0)
                        {
                        LogInfo("ConfigResolvers: (%s) configuring zeroth domain as search list %s", scope ? "Scoped" : "Non-scoped", resolver[0]->domain);
-                       mDNS_AddSearchDomain_CString(resolver[0]->domain);
+                       mDNS_AddSearchDomain_CString(resolver[0]->domain, mDNSNULL);
                        }
                else
                        {
                        for (i = 0; i < resolver[0]->n_search; i++)
                                {
                                LogInfo("ConfigResolvers: (%s) configuring search list %s", scope ? "Scoped" : "Non-scoped", resolver[0]->search[i]);
-                               mDNS_AddSearchDomain_CString(resolver[0]->search[i]);
+                               mDNS_AddSearchDomain_CString(resolver[0]->search[i], mDNSNULL);
                                }
                        }
                }
 
-       if (!setservers) return;
+       // scoped search domains are set below. If neither scoped nor setting servers, we have nothing to do
+       if (!scope && !setservers) return;
 
        // For the "default" resolver ("resolver #1") the "domain" value is bogus and we need to ignore it.
        // e.g. the default resolver's "domain" value might say "apple.com", which indicates that this resolver
@@ -4879,7 +4969,18 @@ mDNSlocal void ConfigResolvers(mDNS *const m, dns_config_t *config, mDNSBool sco
                // Note: Unlike the BSD Sockets APIs (where TCP and UDP port numbers are universally in network byte order)
                // in Apple's "dnsinfo.h" API the port number is declared to be a "uint16_t in host byte order"
                // We also don't need to do any more work if there are no nameserver addresses
-               if (r->port == 5353 || r->n_nameserver == 0) continue;
+               if (r->port == 5353 || r->n_nameserver == 0)
+                       {
+                       char *opt = r->options;
+                       if (opt && !strncmp(opt, "mdns", strlen(opt)))
+                               {
+                               if (!MakeDomainNameFromDNSNameString(&d, r->domain))
+                                       { LogMsg("ConfigResolvers: config->resolver[%d] bad domain %s", i, r->domain); continue; }
+                               mDNS_AddMcastResolver(m, &d, interface, r->timeout);
+                               }
+                       continue;
+                       }
+
                                        
                if (!r->domain || !*r->domain) d.c[0] = 0;
                else if (!MakeDomainNameFromDNSNameString(&d, r->domain))
@@ -4919,11 +5020,10 @@ mDNSlocal void ConfigResolvers(mDNS *const m, dns_config_t *config, mDNSBool sco
                                }
                        }
 
-               // flags and if_index are defined only from this DNSINFO_VERSION onwards. Currently these
-               // fields are zero for non-scoped resolvers. For now, these fields have non-zero values only
-               // for scoped_resolvers.
+               // flags and if_index are defined only from this DNSINFO_VERSION onwards.
+               // Parse the interface index if we have not already parsed one above.
 #if DNSINFO_VERSION >= 20091104
-               if (scope && (r->flags & DNS_RESOLVER_FLAGS_SCOPED) && (r->if_index != 0))
+               if ((interface == mDNSInterface_Any) && (r->if_index != 0))
                        {
                        NetworkInterfaceInfoOSX *ni;
                        interface = mDNSNULL;
@@ -4939,6 +5039,22 @@ mDNSlocal void ConfigResolvers(mDNS *const m, dns_config_t *config, mDNSBool sco
                        }
 #endif
 
+               if (setsearch)
+                       {
+                       // For non-scoped resolvers unlike scoped resolvers, only zeroth resolver has search lists if any. For scoped
+                       // resolvers, we need to parse all the entries.
+                       if (scope)
+                               {
+                               for (j = 0; j < resolver[i]->n_search; j++)
+                                       {
+                                       LogInfo("ConfigResolvers: (%s) configuring search list %s, Interface %p", scope ? "Scoped" : "Non-scoped", resolver[i]->search[j], interface);
+                                       mDNS_AddSearchDomain_CString(resolver[i]->search[j], interface);
+                                       }
+                               // Parse other scoped resolvers for search lists
+                               if (!setservers) continue;
+                               }
+                       }
+
                for (n = 0; n < r->n_nameserver; n++)
                        if (r->nameserver[n]->sa_family == AF_INET || r->nameserver[n]->sa_family == AF_INET6)
                                {
@@ -4951,17 +5067,27 @@ mDNSlocal void ConfigResolvers(mDNS *const m, dns_config_t *config, mDNSBool sco
                                        DNSServer *s;
 #if DNSINFO_VERSION >= 20091104
                                        // By setting scoped, this DNSServer can only be picked if the right interfaceID
-                                       // is given in the question
+                                       // is given in the question
                                        if (scope && (r->flags & DNS_RESOLVER_FLAGS_SCOPED) && (interface == mDNSNULL))
-                                               LogMsg("ConfigResolvers: ERROR: scoped is set but if_index %d is invalid for DNSServer %#a:%d", r->if_index, &saddr, mDNSVal16(r->port ? mDNSOpaque16fromIntVal(r->port) : UnicastDNSPort));
+                                               LogMsg("ConfigResolvers: ERROR: scoped is set but if_index %d is invalid for DNSServer %#a:%d",
+                                                       r->if_index, &saddr, mDNSVal16(r->port ? mDNSOpaque16fromIntVal(r->port) : UnicastDNSPort));
                                        else
                                                scopedDNS = (scope && (r->flags & DNS_RESOLVER_FLAGS_SCOPED)) ? mDNStrue : mDNSfalse;
 #endif
-                                       s = mDNS_AddDNSServer(m, &d, interface, &saddr, r->port ? mDNSOpaque16fromIntVal(r->port) : UnicastDNSPort, scopedDNS);
+                                       // The timeout value is for all the DNS servers in a given resolver, hence we pass
+                                       // the timeout value only for the first DNSServer. If we don't have a value in the
+                                       // resolver, then use the core's default value
+                                       //
+                                       // Note: this assumes that when the core picks a list of DNSServers for a question,
+                                       // it takes the sum of all the timeout values for all DNS servers. By doing this, it
+                                       // tries all the DNS servers in a specified timeout
+                                       s = mDNS_AddDNSServer(m, &d, interface, &saddr, r->port ? mDNSOpaque16fromIntVal(r->port) : UnicastDNSPort, scopedDNS,
+                                               (n == 0 ? (r->timeout ? r->timeout : DEFAULT_UDNS_TIMEOUT) : 0));
                                        if (s)
                                                {
                                                if (disabled) s->teststate = DNSServer_Disabled;
-                                               LogInfo("ConfigResolvers: DNS server %#a:%d for domain %##s from slot %d,%d", &s->addr, mDNSVal16(s->port), d.c, i, n);
+                                               LogInfo("ConfigResolvers: DNS server %#a:%d for domain %##s from slot %d, %d",
+                                                       &s->addr, mDNSVal16(s->port), d.c, i, n);
                                                }
                                        }
                                }
@@ -5019,7 +5145,7 @@ mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDN
                                                                                                                                                         a.ip.v4.b[2] & n.ip.v4.b[2],
                                                                                                                                                         a.ip.v4.b[1] & n.ip.v4.b[1],
                                                                                                                                                         a.ip.v4.b[0] & n.ip.v4.b[0]);
-                               mDNS_AddSearchDomain_CString(buf);
+                               mDNS_AddSearchDomain_CString(buf, mDNSNULL);
                                }
                        ifa = ifa->ifa_next;
                        }
@@ -5036,7 +5162,7 @@ mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDN
                        // Apparently this is expected behaviour -- "not a bug".
                        // Accordingly, we suppress syslog messages for the first three minutes after boot.
                        // If we are still getting failures after three minutes, then we log them.
-                       if (OSXVers > OSXVers_10_3_Panther && (mDNSu32)mDNSPlatformRawTime() > (mDNSu32)(mDNSPlatformOneSecond * 180))
+                       if ((mDNSu32)mDNSPlatformRawTime() > (mDNSu32)(mDNSPlatformOneSecond * 180))
                                LogMsg("mDNSPlatformSetDNSConfig: Error: dns_configuration_copy returned NULL");
                        }
                else
@@ -5066,6 +5192,7 @@ mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDN
                        ConfigResolvers(m, config, mDNStrue, setsearch, setservers);
 #endif
                        dns_configuration_free(config);
+                       if (setsearch) RetrySearchDomainQuestions(m);
                        setservers = mDNSfalse;  // Done these now -- no need to fetch the same data from SCDynamicStore
                        setsearch  = mDNSfalse;
                        }
@@ -5203,7 +5330,7 @@ mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDN
                                                                inet_aton(buf, (struct in_addr *) &addr.ip.v4))
                                                                {
                                                                LogInfo("Adding DNS server from dict: %s", buf);
-                                                               mDNS_AddDNSServer(m, mDNSNULL, mDNSInterface_Any, &addr, UnicastDNSPort, mDNSfalse);
+                                                               mDNS_AddDNSServer(m, mDNSNULL, mDNSInterface_Any, &addr, UnicastDNSPort, mDNSfalse, 0);
                                                                }
                                                        }
                                                }
@@ -5219,7 +5346,7 @@ mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDN
                                                        {
                                                        CFStringRef s = CFArrayGetValueAtIndex(searchDomains, i);
                                                        if (s && CFStringGetCString(s, buf, sizeof(buf), kCFStringEncodingUTF8))
-                                                               mDNS_AddSearchDomain_CString(buf);
+                                                               mDNS_AddSearchDomain_CString(buf, mDNSNULL);
                                                        }
                                                }
                                        else    // No kSCPropNetDNSSearchDomains, so use kSCPropNetDNSDomainName
@@ -5231,8 +5358,9 @@ mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDN
                                                // (and the domain from the "domain" field will also appear somewhere in that list).
                                                CFStringRef string = CFDictionaryGetValue(dict, kSCPropNetDNSDomainName);
                                                if (string && CFStringGetCString(string, buf, sizeof(buf), kCFStringEncodingUTF8))
-                                                       mDNS_AddSearchDomain_CString(buf);
+                                                       mDNS_AddSearchDomain_CString(buf, mDNSNULL);
                                                }
+                                       RetrySearchDomainQuestions(m);
                                        }
                                CFRelease(dict);
                                }
@@ -5566,7 +5694,7 @@ mDNSexport void SetDomainSecrets(mDNS *m)
        if (!sa) { LogMsg("SetDomainSecrets: CFArrayCreateMutable failed"); return; }
        CFIndex i;
        CFDataRef data = NULL;
-       const int itemsPerEntry = 3; // domain name, key name, key value
+       const int itemsPerEntry = 4; // domain name, key name, key value, Name value
        CFArrayRef secrets = NULL;
        int err = mDNSKeychainGetSecrets(&secrets);
        if (err || !secrets)
@@ -5577,29 +5705,40 @@ mDNSexport void SetDomainSecrets(mDNS *m)
                // Iterate through the secrets
                for (i = 0; i < ArrayCount; ++i)
                        {
-                       int j;
+                       mDNSBool AutoTunnel;
+                       int j, offset;
                        CFArrayRef entry = CFArrayGetValueAtIndex(secrets, i);
                        if (CFArrayGetTypeID() != CFGetTypeID(entry) || itemsPerEntry != CFArrayGetCount(entry))
-                               { LogMsg("SetDomainSecrets: malformed entry"); continue; }
+                               { LogMsg("SetDomainSecrets: malformed entry %d, itemsPerEntry %d", i, itemsPerEntry); continue; }
                        for (j = 0; j < CFArrayGetCount(entry); ++j)
                                if (CFDataGetTypeID() != CFGetTypeID(CFArrayGetValueAtIndex(entry, j)))
-                                       { LogMsg("SetDomainSecrets: malformed entry item"); continue; }
+                                       { LogMsg("SetDomainSecrets: malformed entry item %d", j); continue; }
 
                        // The names have already been vetted by the helper, but checking them again here helps humans and automated tools verify correctness
 
-                       // Get DNS domain this key is for
-                       char stringbuf[MAX_ESCAPED_DOMAIN_NAME];        // Max legal domainname as C-string, including terminating NUL
-                       data = CFArrayGetValueAtIndex(entry, 0);
+                       // Max legal domainname as C-string, including space for btmmprefix and terminating NUL
+                       // Get DNS domain this key is for (kmDNSKcWhere)
+                       char stringbuf[MAX_ESCAPED_DOMAIN_NAME + sizeof(btmmprefix)];
+                       data = CFArrayGetValueAtIndex(entry, kmDNSKcWhere);
                        if (CFDataGetLength(data) >= (int)sizeof(stringbuf))
                                { LogMsg("SetDomainSecrets: Bad kSecServiceItemAttr length %d", CFDataGetLength(data)); continue; }
                        CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (UInt8 *)stringbuf);
                        stringbuf[CFDataGetLength(data)] = '\0';
 
+                       AutoTunnel = mDNSfalse;
+                       offset = 0;
+                       if (!strncmp(stringbuf, dnsprefix, strlen(dnsprefix)))
+                               offset = strlen(dnsprefix);
+                       else if (!strncmp(stringbuf, btmmprefix, strlen(btmmprefix)))
+                               {
+                               AutoTunnel = mDNStrue;
+                               offset = strlen(btmmprefix);
+                               }
                        domainname domain;
-                       if (!MakeDomainNameFromDNSNameString(&domain, stringbuf)) { LogMsg("SetDomainSecrets: bad key domain %s", stringbuf); continue; }
+                       if (!MakeDomainNameFromDNSNameString(&domain, stringbuf + offset)) { LogMsg("SetDomainSecrets: bad key domain %s", stringbuf); continue; }
 
-                       // Get key name
-                       data = CFArrayGetValueAtIndex(entry, 1);
+                       // Get key name (kmDNSKcAccount)
+                       data = CFArrayGetValueAtIndex(entry, kmDNSKcAccount);
                        if (CFDataGetLength(data) >= (int)sizeof(stringbuf))
                                { LogMsg("SetDomainSecrets: Bad kSecAccountItemAttr length %d", CFDataGetLength(data)); continue; }
                        CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8 *)stringbuf);
@@ -5608,13 +5747,53 @@ mDNSexport void SetDomainSecrets(mDNS *m)
                        domainname keyname;
                        if (!MakeDomainNameFromDNSNameString(&keyname, stringbuf)) { LogMsg("SetDomainSecrets: bad key name %s", stringbuf); continue; }
 
-                       // Get key data
-                       data = CFArrayGetValueAtIndex(entry, 2);
+                       // Get key data (kmDNSKcKey)
+                       data = CFArrayGetValueAtIndex(entry, kmDNSKcKey);
                        if (CFDataGetLength(data) >= (int)sizeof(stringbuf))
                                { LogMsg("SetDomainSecrets: Shared secret too long: %d", CFDataGetLength(data)); continue; }
                        CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (UInt8 *)stringbuf);
                        stringbuf[CFDataGetLength(data)] = '\0';        // mDNS_SetSecretForDomain requires NULL-terminated C string for key
 
+                       // Get the Name of the keychain entry (kmDNSKcName) host or host:port
+                       // The hostname also has the port number and ":". It should take a maximum of 6 bytes.
+                       char hostbuf[MAX_ESCAPED_DOMAIN_NAME + 6];      // Max legal domainname as C-string, including terminating NUL
+                       data = CFArrayGetValueAtIndex(entry, kmDNSKcName);
+                       if (CFDataGetLength(data) >= (int)sizeof(hostbuf))
+                               { LogMsg("SetDomainSecrets: Shared secret too long: %d", CFDataGetLength(data)); continue; }
+                       CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8 *)hostbuf);
+                       hostbuf[CFDataGetLength(data)] = '\0';
+
+                       domainname hostname;
+                       mDNSIPPort port;
+                       char *hptr;
+                       hptr = strchr(hostbuf, ':');
+                       
+                       port.NotAnInteger = 0;
+                       if (hptr)
+                               {
+                               mDNSu8 *p;
+                               mDNSu16 val = 0;
+
+                               *hptr++ = '\0'; 
+                               while(hptr && *hptr != 0)
+                                       {
+                                       if (*hptr < '0' || *hptr > '9')
+                                               { LogMsg("SetDomainSecrets: Malformed Port number %d, val %d", *hptr, val); val = 0; break;}
+                                       val = val * 10 + *hptr - '0';
+                                       hptr++;
+                                       }
+                               if (!val) continue;
+                               p = (mDNSu8 *)&val;
+                               port.NotAnInteger = p[0] << 8 | p[1];
+                               }
+                       // The hostbuf is of the format dsid@hostname:port. We don't care about the dsid. 
+                       hptr = strchr(hostbuf, '@');
+                       if (hptr)
+                               hptr++;
+                       else
+                               hptr = hostbuf;
+                       if (!MakeDomainNameFromDNSNameString(&hostname, hptr)) { LogMsg("SetDomainSecrets: bad host name %s", hptr); continue; }
+
                        DomainAuthInfo *FoundInList;
                        for (FoundInList = m->AuthInfoList; FoundInList; FoundInList = FoundInList->next)
                                if (SameDomainName(&FoundInList->domain, &domain)) break;
@@ -5636,7 +5815,8 @@ mDNSexport void SetDomainSecrets(mDNS *m)
 
                        // Uncomment the line below to view the keys as they're read out of the system keychain
                        // DO NOT SHIP CODE THIS WAY OR YOU'LL LEAK SECRET DATA INTO A PUBLICLY READABLE FILE!
-                       //LogInfo("SetDomainSecrets: %##s %##s %s", &domain.c, &keyname.c, stringbuf);
+                       //LogInfo("SetDomainSecrets: domain %##s keyname %##s key %s hostname %##s port %d", &domain.c, &keyname.c, stringbuf, hostname.c, (port.b[0] << 8 | port.b[1]));
+                       LogInfo("SetDomainSecrets: domain %##s keyname %##s hostname %##s port %d", &domain.c, &keyname.c, hostname.c, (port.b[0] << 8 | port.b[1]));
 
                        // If didn't find desired domain in the list, make a new entry
                        ptr = FoundInList;
@@ -5647,8 +5827,10 @@ mDNSexport void SetDomainSecrets(mDNS *m)
                                if (!ptr) { LogMsg("SetDomainSecrets: No memory"); continue; }
                                }
 
-                       LogInfo("SetDomainSecrets: %d of %d %##s", i, ArrayCount, &domain);
-                       if (mDNS_SetSecretForDomain(m, ptr, &domain, &keyname, stringbuf, IsTunnelModeDomain(&domain)) == mStatus_BadParamErr)
+                       //LogInfo("SetDomainSecrets: %d of %d %##s", i, ArrayCount, &domain);
+
+                       // It is an AutoTunnel if the keychains tells us so (with btmm prefix) or if it is a TunnelModeDomain
+                       if (mDNS_SetSecretForDomain(m, ptr, &domain, &keyname, stringbuf, &hostname, &port, (AutoTunnel ? btmmprefix : (IsTunnelModeDomain(&domain) ? dnsprefix : NULL))) == mStatus_BadParamErr)
                                {
                                if (!FoundInList) mDNSPlatformMemFree(ptr);             // If we made a new DomainAuthInfo here, and it turned out bad, dispose it immediately
                                continue;
@@ -5968,18 +6150,23 @@ mDNSlocal void SetSPS(mDNS *const m)
        SCPreferencesSynchronize(m->p->SCPrefs);
        CFDictionaryRef dict = SCPreferencesGetValue(m->p->SCPrefs, CFSTR("NAT"));
        mDNSBool natenabled = (dict && (CFGetTypeID(dict) == CFDictionaryGetTypeID()) && DictionaryIsEnabled(dict));
-       mDNSu8 sps = natenabled ? 50 : (OfferSleepProxyService && GetSystemSleepTimerSetting() == 0) ? OfferSleepProxyService : 0;
+       mDNSu8 sps = natenabled ? mDNSSleepProxyMetric_PrimarySoftware :
+               (OfferSleepProxyService && GetSystemSleepTimerSetting() == 0) ? mDNSSleepProxyMetric_IncidentalSoftware : 0;
 
        // For devices that are not running NAT, but are set to never sleep, we may choose to act
        // as a Sleep Proxy, but only for non-portable Macs (Portability > 35 means nominal weight < 3kg)
-       if (sps > 50 && SPMetricPortability > 35) sps = 0;
+       //if (sps > mDNSSleepProxyMetric_PrimarySoftware && SPMetricPortability > 35) sps = 0;
 
        // If we decide to let laptops act as Sleep Proxy, we should do it only when running on AC power, not on battery
 
-       // For devices that are unable to sleep at all to save power (e.g. the current Apple TV hardware)
+       // For devices that are unable to sleep at all to save power, or save 1W or less by sleeping,
        // it makes sense for them to offer low-priority Sleep Proxy service on the network.
        // We rate such a device as metric 70 ("Incidentally Available Hardware")
-       if (SPMetricMarginalPower == 10 && (!sps || sps > 70)) sps = 70;
+       if (SPMetricMarginalPower <= 60 && !sps) sps = mDNSSleepProxyMetric_IncidentalHardware;
+
+       // If the launchd plist specifies an explicit value for the Intent Metric, then use that instead of the
+       // computed value (currently 40 "Primary Network Infrastructure Software" or 80 "Incidentally Available Software")
+       if (sps && OfferSleepProxyService && OfferSleepProxyService < 100) sps = OfferSleepProxyService;
 
        mDNSCoreBeSleepProxyServer(m, sps, SPMetricPortability, SPMetricMarginalPower, SPMetricTotalPower);
        }
@@ -6013,7 +6200,7 @@ mDNSlocal mStatus WatchForInternetSharingChanges(mDNS *const m)
        if (!SCPreferencesSetCallback(SCPrefs, InternetSharingChanged, &context))
                { LogMsg("SCPreferencesSetCallback failed: %s", SCErrorString(SCError())); CFRelease(SCPrefs); return(mStatus_NoMemoryErr); }
 
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
        if (!SCPreferencesSetDispatchQueue( SCPrefs, dispatch_get_main_queue()))
                { LogMsg("SCPreferencesSetDispatchQueue failed: %s", SCErrorString(SCError())); return(mStatus_NoMemoryErr); }
 #else
@@ -6079,8 +6266,7 @@ mDNSlocal mDNSu16 GetPortArray(mDNS *const m, int trans, mDNSIPPort *portarray)
        }
 
 #define TfrRecordToNIC(RR) \
-       (((RR)->resrec.InterfaceID && (RR)->resrec.InterfaceID != mDNSInterface_LocalOnly) || \
-       (!(RR)->resrec.InterfaceID && ((RR)->ForceMCast || IsLocalDomain((RR)->resrec.name))))
+       ((!(RR)->resrec.InterfaceID && ((RR)->ForceMCast || IsLocalDomain((RR)->resrec.name))))
 
 mDNSlocal mDNSu32 CountProxyRecords(mDNS *const m, uint32_t *const numbytes)
        {
@@ -6169,6 +6355,8 @@ mDNSexport mStatus ActivateLocalProxy(mDNS *const m, char *ifname)        // Called wit
                        if (CFGetTypeID(ref) != CFStringGetTypeID() || !CFEqual(ref, CFSTR(mDNS_IOREG_VALUE)))
                                LogMsg("ActivateLocalProxy: mDNS_IOREG_KEY for interface %s/%s/%s value %s != %s",
                                        ifname, n1, n2, CFStringGetCStringPtr(ref, mDNSNULL), mDNS_IOREG_VALUE);
+                       else if (!UseInternalSleepProxy)
+                               LogSPS("ActivateLocalProxy: Not using internal (NIC) sleep proxy for interface %s", ifname);
                        else
                                {
                                io_connect_t conObj;
@@ -6266,7 +6454,7 @@ mDNSlocal mDNSBool SystemWakeForNetworkAccess(void)
 mDNSlocal mDNSBool SystemSleepOnlyIfWakeOnLAN(void)
        {
        mDNSs32 val = 0;
-       GetCurrentPMSetting(CFSTR("Idle Sleep Requires Network Proxy"), &val);
+       GetCurrentPMSetting(CFSTR("PrioritizeNetworkReachabilityOverSleep"), &val);
        return val != 0 ? mDNStrue : mDNSfalse;
        }
 
@@ -6496,6 +6684,9 @@ mDNSlocal void ParseBackToMyMac(mDNS *const m, CFDictionaryRef connd)
                // Note: AutoTunnelRelayAddrIn is zeroed out in RemoveAutoTunnel6Record as it is called
                // from other places.
                m->AutoTunnelRelayAddrOut = zerov6Addr;
+               mDNS_Lock(m);
+               UpdateAutoTunnelDomainStatuses(m);
+               mDNS_Unlock(m);
                return;
                }
 
@@ -6513,9 +6704,13 @@ mDNSlocal void ParseBackToMyMac(mDNS *const m, CFDictionaryRef connd)
                LogInfo("ParseBackToMyMac: non-NULL value for Interface, Adding autotunnel6"); 
                AddAutoTunnel6Record(m, buf, BTMMDict);
                }
+
+       mDNS_Lock(m);
+       UpdateAutoTunnelDomainStatuses(m);
+       mDNS_Unlock(m);
        }
 
-mDNSexport void SetupConndConfigChanges(mDNS *const m)
+mDNSlocal void SetupConndConfigChanges(mDNS *const m)
        {
        CFDictionaryRef connd;
        SCDynamicStoreRef store;
@@ -6629,6 +6824,16 @@ mDNSlocal void SetNetworkChanged(mDNS *const m, mDNSs32 delay)
                }
        }
 
+// Called with KQueueLock & mDNS lock
+mDNSlocal void SetKeyChainTimer(mDNS *const m, mDNSs32 delay)
+       {
+       // If it's not set or it needs to happen sooner than when it's currently set
+       if (!m->p->KeyChainTimer || m->p->KeyChainTimer - NonZeroTime(m->timenow + delay) > 0)
+               {
+               m->p->KeyChainTimer = NonZeroTime(m->timenow + delay);
+               LogInfo("SetKeyChainTimer: %d", delay);
+               }
+       }
 
 // Copy the fourth slash-delimited element from either:
 //   State:/Network/Interface/<bsdname>/IPv4
@@ -6767,6 +6972,7 @@ mDNSlocal mDNSBool ChangedKeysHaveIPv4LL(CFArrayRef inkeys)
 mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context)
        {
        (void)store;        // Parameter not used
+       mDNSBool changeNow = mDNSfalse;
        mDNS *const m = (mDNS *const)context;
        KQueueLock(m);
        mDNS_Lock(m);
@@ -6781,6 +6987,24 @@ mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, v
        int c4 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_DNS         ) != 0);
        if (c && c - c1 - c2 - c3 - c4 == 0) delay = mDNSPlatformOneSecond/10;  // If these were the only changes, shorten delay
        
+       {
+       int i;
+       for (i=0; i<c; i++)
+               {
+               char buf[256];
+               if (!CFStringGetCString(CFArrayGetValueAtIndex(changedKeys, i), buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0;
+               if (buf[0])
+                       {
+                       if (strstr(buf, "p2p"))
+                               {
+                               LogInfo("NetworkChanged SC key: %s, not delaying network change", buf);
+                               changeNow = mDNStrue;
+                               break;
+                               }
+                       }
+               }
+       }
+
        if (mDNS_LoggingEnabled)
                {
                int i;
@@ -6799,28 +7023,90 @@ mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, v
                        delay);
                }
 
+       mDNSBool btmmChanged = CFArrayContainsValue(changedKeys, range, NetworkChangedKey_BackToMyMac);
+       if (btmmChanged) delay = 0;
+
        SetNetworkChanged(m, delay);
        
-       // KeyChain frequently fails to notify clients of change events. To work around this
-       // we set a timer and periodically poll to detect if any changes have occurred.
-       // Without this Back To My Mac just does't work for a large number of users.
-       // See <rdar://problem/5124399> Not getting Keychain Changed events when enabling BTMM
-       if (c3 || CFArrayContainsValue(changedKeys, range, NetworkChangedKey_BackToMyMac))
-               {
-               LogInfo("***   NetworkChanged   *** starting KeyChainBugTimer");
-               m->p->KeyChainBugTimer    = NonZeroTime(m->timenow + delay);
-               m->p->KeyChainBugInterval = mDNSPlatformOneSecond;
-               }
+       // Other software might pick up these changes to register or browse in WAB or BTMM domains,
+       // so in order for secure updates to be made to the server, make sure to read the keychain and
+       // setup the DomainAuthInfo before handing the network change.
+       // If we don't, then we will first try to register services in the clear, then later setup the
+       // DomainAuthInfo, which is incorrect.
+       if (c3 || btmmChanged)
+               SetKeyChainTimer(m, delay);
 
        mDNS_Unlock(m);
 
        // If DNS settings changed, immediately force a reconfig (esp. cache flush)
        // Similarly, if an interface changed that is explicitly IPv4 link local, immediately force a reconfig
-       if (c4 || ChangedKeysHaveIPv4LL(changedKeys)) mDNSMacOSXNetworkChanged(m);
+       if (c4 || ChangedKeysHaveIPv4LL(changedKeys) || changeNow) mDNSMacOSXNetworkChanged(m);
 
        KQueueUnlock(m, "NetworkChanged");
        }
 
+#if APPLE_OSX_mDNSResponder
+mDNSlocal void RefreshSPSStatus(const void *key, const void *value, void *context)
+       {
+       (void)context;
+       char buf[IFNAMSIZ];
+
+       CFStringRef ifnameStr = (CFStringRef)key;
+       CFArrayRef array = (CFArrayRef)value;
+       if (!CFStringGetCString(ifnameStr, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0;
+
+       LogInfo("RefreshSPSStatus: Updating SPS state for key %s, array count %d", buf, CFArrayGetCount(array));
+       mDNSDynamicStoreSetConfig(kmDNSSleepProxyServersState, buf, value);
+       }
+#endif
+
+mDNSlocal void DynamicStoreReconnected(SCDynamicStoreRef store, void *info)
+       {
+       mDNS *const m = (mDNS *const)info;
+       (void)store;
+
+       LogInfo("DynamicStoreReconnected: Reconnected");
+
+       // State:/Network/MulticastDNS
+       SetLocalDomains();
+
+       // State:/Network/DynamicDNS
+       if (m->FQDN.c[0])
+               mDNSPlatformDynDNSHostNameStatusChanged(&m->FQDN, 1);
+
+       // Note: PrivateDNS and BackToMyMac are automatically populated when configd is restarted
+       // as we receive network change notifications and thus not necessary. But we leave it here
+       // so that if things are done differently in the future, this code still works.
+
+       // State:/Network/PrivateDNS
+       CFMutableArrayRef sa = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+       if (!sa)
+               LogMsg("DynamicStoreReconnected:PrivateDNS: CFArrayCreateMutable failed");
+       else
+               {
+               DomainAuthInfo *FoundInList;
+               char stringbuf[MAX_ESCAPED_DOMAIN_NAME];        // Max legal domainname as C-string, including terminating NUL
+               for (FoundInList = m->AuthInfoList; FoundInList; FoundInList = FoundInList->next)
+                       {
+                       ConvertDomainNameToCString(&FoundInList->domain, stringbuf);
+                       CFStringRef cfs = CFStringCreateWithCString(NULL, stringbuf, kCFStringEncodingUTF8);
+                       if (cfs) { CFArrayAppendValue(sa, cfs); CFRelease(cfs); }
+                       }
+               mDNSDynamicStoreSetConfig(kmDNSPrivateConfig, mDNSNULL, sa);
+               CFRelease(sa);
+               }
+
+       // State:/Network/BackToMyMac
+#if APPLE_OSX_mDNSResponder
+       mDNS_Lock(m);   
+       UpdateAutoTunnelDomainStatuses(m);
+       mDNS_Unlock(m); 
+
+       // State:/Network/Interface/en0/SleepProxyServers
+       if (spsStatusDict) CFDictionaryApplyFunction(spsStatusDict, RefreshSPSStatus, NULL);
+#endif
+       }
+
 mDNSlocal mStatus WatchForNetworkChanges(mDNS *const m)
        {
        mStatus err = -1;
@@ -6849,7 +7135,7 @@ mDNSlocal mStatus WatchForNetworkChanges(mDNS *const m)
        if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns))
                { LogMsg("SCDynamicStoreSetNotificationKeys failed: %s", SCErrorString(SCError())); goto error; }
 
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
        if (!SCDynamicStoreSetDispatchQueue(store, dispatch_get_main_queue()))
                { LogMsg("SCDynamicStoreCreateRunLoopSource failed: %s", SCErrorString(SCError())); goto error; }
 #else
@@ -6857,6 +7143,7 @@ mDNSlocal mStatus WatchForNetworkChanges(mDNS *const m)
        if (!m->p->StoreRLS) { LogMsg("SCDynamicStoreCreateRunLoopSource failed: %s", SCErrorString(SCError())); goto error; }
        CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode);
 #endif
+       SCDynamicStoreSetDisconnectCallBack(store, DynamicStoreReconnected);
        m->p->Store = store;
        err = 0;
        goto exit;
@@ -6904,6 +7191,88 @@ mDNSlocal mStatus WatchForPMChanges(mDNS *const m)
 #define KEV_DL_WAKEFLAGS_CHANGED 17
 #endif
 
+#if !TARGET_OS_EMBEDDED                // don't setup packet filter rules on embedded
+
+mDNSlocal void mDNSSetPacketFilterRules(mDNS *const m, char * ifname)
+       {
+       AuthRecord *rr;
+       int     found = 0;
+       
+       for (rr = m->ResourceRecords; rr; rr=rr->next)
+               {
+               if ((rr->resrec.rrtype == kDNSServiceType_SRV) && (rr->ARType == AuthRecordAnyIncludeP2P))
+                       {
+                       uint16_t                port = rr->resrec.rdata->u.srv.port.NotAnInteger;
+                       uint16_t                protocol;
+                       const mDNSu8    *p;
+
+                       LogInfo("mDNSSetPacketFilterRules: found %s", ARDisplayString(m, rr));
+
+                       // Note presence of more than one p2p service in list
+                       // since code is currently opening only one port in the packet filter.
+                       found++;
+                       if (found > 1)
+                               {
+                               LogMsg("mDNSSetPacketFilterRules: found service #%d %s", found, ARDisplayString(m, rr));
+                               }
+
+                       // Assume <Service Instance>.<App Protocol>.<Transport Protocol>.<Name>
+                       p = rr->resrec.name->c;
+
+                       // Skip to App Protocol
+                       if (p[0]) p += 1 + p[0];
+                       // Skip to Transport Protocol
+                       if (p[0]) p += 1 + p[0];
+
+                       if      (SameDomainLabel(p, (mDNSu8 *)"\x4" "_tcp")) protocol = IPPROTO_TCP;
+                       else if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_udp")) protocol = IPPROTO_UDP;
+                       else 
+                               { 
+                               LogMsg("mDNSSetPacketFilterRules: could not determine transport protocol of service");
+                               LogMsg("mDNSSetPacketFilterRules: %s", ARDisplayString(m, rr));
+                               return;
+                               }
+
+                       LogInfo("mDNSSetPacketFilterRules: ifname %s, port(in NBO)0x%X, protocol %d",
+                                       ifname, port, protocol);
+                       mDNSPacketFilterControl(PF_SET_RULES, ifname, port, protocol);
+                       }
+               }
+       }
+#endif // !TARGET_OS_EMBEDDED
+
+
+#if APPLE_OSX_mDNSResponder    
+
+#if !TARGET_OS_EMBEDDED        
+// If the p2p interface already exists, set the Bonjour packet filter rules for it.
+mDNSexport void mDNSInitPacketFilter(void)
+       {
+       mDNS *const m = & mDNSStorage;
+
+       NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces);
+       while (intf)
+               {
+               if (strncmp(intf->ifname, "p2p", 3) == 0)
+                       {
+                       LogInfo("mDNSInitPacketFilter: Setting rules for ifname %s", intf->ifname);
+                       mDNSSetPacketFilterRules(m, intf->ifname);
+                       break;
+                       }
+               intf = GetFirstActiveInterface(intf->next);
+               }
+       }
+
+#else // !TARGET_OS_EMBEDDED
+
+       // Currently no packet filter setup required embedded on platforms.
+mDNSexport void mDNSInitPacketFilter()
+       {
+       }
+
+#endif // !TARGET_OS_EMBEDDED
+#endif // APPLE_OSX_mDNSResponder
+
 mDNSlocal void SysEventCallBack(int s1, short __unused filter, void *context)
        {
        mDNS *const m = (mDNS *const)context;
@@ -6948,6 +7317,44 @@ mDNSlocal void SysEventCallBack(int s1, short __unused filter, void *context)
 
                if (msg.k.event_code == KEV_DL_WAKEFLAGS_CHANGED || msg.k.event_code == KEV_DL_LINK_ON)
                        SetNetworkChanged(m, mDNSPlatformOneSecond * 2);
+
+#if !TARGET_OS_EMBEDDED                // don't setup packet filter rules on embedded
+
+               // For p2p interfaces, need to open the advertised service port in the firewall.
+               if (msg.k.event_code == KEV_DL_IF_ATTACHED)
+                       {
+                       struct net_event_data   * p;
+                       p = (struct net_event_data *) & msg.k.event_data;
+
+                       if (strncmp(p->if_name, "p2p", 3) == 0)
+                               {
+                               char    ifname[IFNAMSIZ];
+                               snprintf(ifname, IFNAMSIZ, "%s%d", p->if_name, p->if_unit);
+
+                               LogInfo("SysEventCallBack: KEV_DL_IF_ATTACHED if_family = %d, if_unit = %d, if_name = %s", p->if_family, p->if_unit, p->if_name);
+
+                               mDNSSetPacketFilterRules(m, ifname);
+                               }
+                       }
+
+               // For p2p interfaces, need to clear the firewall rules on interface detach
+               if (msg.k.event_code == KEV_DL_IF_DETACHED)
+                       {
+                       struct net_event_data   * p;
+                       p = (struct net_event_data *) & msg.k.event_data;
+
+                       if (strncmp(p->if_name, "p2p", 3) == 0)
+                               {
+                               char    ifname[IFNAMSIZ];
+                               snprintf(ifname, IFNAMSIZ, "%s%d", p->if_name, p->if_unit);
+
+                               LogInfo("SysEventCallBack: KEV_DL_IF_DETACHED if_family = %d, if_unit = %d, if_name = %s", p->if_family, p->if_unit, p->if_name);
+
+                               mDNSPacketFilterControl(PF_CLEAR_RULES, ifname, 0, 0);
+                               }
+                       }
+#endif // !TARGET_OS_EMBEDDED
+
                }
 
        mDNS_Unlock(m);
@@ -6999,7 +7406,8 @@ mDNSlocal OSStatus KeychainChanged(SecKeychainEvent keychainEvent, SecKeychainCa
                                if (!err)
                                        {
                                        relevant = ((a->attr[0].length == 4 && (!strncasecmp(a->attr[0].data, "ddns", 4) || !strncasecmp(a->attr[0].data, "sndd", 4))) ||
-                                                               (a->attr[1].length >= 4 && (!strncasecmp(a->attr[1].data, "dns:", 4))));
+                                                               (a->attr[1].length >= mDNSPlatformStrLen(dnsprefix) && (!strncasecmp(a->attr[1].data, dnsprefix, mDNSPlatformStrLen(dnsprefix)))) || 
+                                                               (a->attr[1].length >= mDNSPlatformStrLen(btmmprefix) && (!strncasecmp(a->attr[1].data, btmmprefix, mDNSPlatformStrLen(btmmprefix)))));
                                        SecKeychainItemFreeAttributesAndData(a, NULL);
                                        }
                                }
@@ -7013,7 +7421,20 @@ mDNSlocal OSStatus KeychainChanged(SecKeychainEvent keychainEvent, SecKeychainCa
                                // We're running on the CFRunLoop (Mach port) thread, not the kqueue thread, so we need to grab the KQueueLock before proceeding
                                KQueueLock(m);
                                mDNS_Lock(m);
-                               SetDomainSecrets(m);
+
+                               // To not read the keychain twice: when BTMM is enabled, changes happen to the keychain
+                               // then the BTMM DynStore dictionary, so delay reading the keychain for a second.
+                               // NetworkChanged() will reset the keychain timer to fire immediately when the DynStore changes.
+                               //
+                               // In the "fixup" case where the BTMM DNS servers aren't accepting the key mDNSResponder has,
+                               // the DynStore dictionary won't change (because the BTMM zone won't change).  In that case,
+                               // a one second delay is ok, as we'll still converge to correctness, and there's no race
+                               // condition between the RegistrationDomain and the DomainAuthInfo.
+                               //
+                               // Lastly, non-BTMM WAB cases can use the keychain but not the DynStore, so we need to set
+                               // the timer here, as it will not get set by NetworkChanged().
+                               SetKeyChainTimer(m, mDNSPlatformOneSecond);
+
                                mDNS_Unlock(m);
                                KQueueUnlock(m, "KeychainChanged");
                                }
@@ -7032,15 +7453,13 @@ mDNSlocal void PowerOn(mDNS *const m)
                {
                long utc = mDNSPlatformUTC();
                mDNSPowerRequest(-1,-1);                // Need to explicitly clear any previous power requests -- they're not cleared automatically on wake
-               if      (m->p->WakeAtUTC - utc > 30) LogInfo("PowerChanged PowerOn %d seconds early, assuming not maintenance wake", m->p->WakeAtUTC - utc);
-               else if (utc - m->p->WakeAtUTC > 30) LogInfo("PowerChanged PowerOn %d seconds late, assuming not maintenance wake", utc - m->p->WakeAtUTC);
+               if      (m->p->WakeAtUTC - utc > 30)               LogSPS("PowerChanged PowerOn %d seconds early, assuming not maintenance wake",     m->p->WakeAtUTC - utc);
+               else if (utc - m->p->WakeAtUTC > 30)               LogSPS("PowerChanged PowerOn %d seconds late, assuming not maintenance wake",      utc - m->p->WakeAtUTC);
+               else if (!strncasecmp(HINFO_HWstring, "K66AP", 5)) LogSPS("PowerChanged PowerOn %d seconds late, device is K66AP so not re-sleeping", utc - m->p->WakeAtUTC);
                else
                        {
-                       mDNSs32 i = 20;
-                       //int result = mDNSPowerRequest(0, i);
-                       //if (result == kIOReturnNotReady) do i += (i<20) ? 1 : ((i+3)/4); while (mDNSPowerRequest(0, i) == kIOReturnNotReady);
-                       LogMsg("PowerChanged: Waking for network maintenance operations %d seconds early; re-sleeping in %d seconds", m->p->WakeAtUTC - utc, i);
-                       m->p->RequestReSleep = mDNS_TimeNow(m) + i * mDNSPlatformOneSecond;
+                       LogSPS("PowerChanged: Waking for network maintenance operations %d seconds early; re-sleeping in 20 seconds", m->p->WakeAtUTC - utc);
+                       m->p->RequestReSleep = mDNS_TimeNow(m) + 20 * mDNSPlatformOneSecond;
                        }
                }
        }
@@ -7057,18 +7476,18 @@ mDNSlocal void PowerChanged(void *refcon, io_service_t service, natural_t messag
 
        switch(messageType)
                {
-               case kIOMessageCanSystemPowerOff:               LogInfo("PowerChanged kIOMessageCanSystemPowerOff     (no action)");    break;  // E0000240
-               case kIOMessageSystemWillPowerOff:              LogInfo("PowerChanged kIOMessageSystemWillPowerOff");                                                   // E0000250
+               case kIOMessageCanSystemPowerOff:               LogSPS("PowerChanged kIOMessageCanSystemPowerOff     (no action)");     break;          // E0000240
+               case kIOMessageSystemWillPowerOff:              LogSPS("PowerChanged kIOMessageSystemWillPowerOff");                                                    // E0000250
                                                                                                mDNSCoreMachineSleep(m, true);
                                                                                                if (m->SleepState == SleepState_Sleeping) mDNSMacOSXNetworkChanged(m);
                                                                                                break;
-               case kIOMessageSystemWillNotPowerOff:   LogInfo("PowerChanged kIOMessageSystemWillNotPowerOff (no action)");    break;  // E0000260
-               case kIOMessageCanSystemSleep:                  LogInfo("PowerChanged kIOMessageCanSystemSleep        (no action)");    break;  // E0000270
-               case kIOMessageSystemWillSleep:                 LogInfo("PowerChanged kIOMessageSystemWillSleep");                                                              // E0000280
+               case kIOMessageSystemWillNotPowerOff:   LogSPS("PowerChanged kIOMessageSystemWillNotPowerOff (no action)");     break;          // E0000260
+               case kIOMessageCanSystemSleep:                  LogSPS("PowerChanged kIOMessageCanSystemSleep        (no action)");     break;          // E0000270
+               case kIOMessageSystemWillSleep:                 LogSPS("PowerChanged kIOMessageSystemWillSleep");                                                               // E0000280
                                                                                                mDNSCoreMachineSleep(m, true);
                                                                                                break;
-               case kIOMessageSystemWillNotSleep:              LogInfo("PowerChanged kIOMessageSystemWillNotSleep    (no action)");    break;  // E0000290
-               case kIOMessageSystemHasPoweredOn:              LogInfo("PowerChanged kIOMessageSystemHasPoweredOn");                                                   // E0000300
+               case kIOMessageSystemWillNotSleep:              LogSPS("PowerChanged kIOMessageSystemWillNotSleep    (no action)");     break;          // E0000290
+               case kIOMessageSystemHasPoweredOn:              LogSPS("PowerChanged kIOMessageSystemHasPoweredOn");                                                    // E0000300
                                                                                                // If still sleeping (didn't get 'WillPowerOn' message for some reason?) wake now
                                                                                                if (m->SleepState)
                                                                                                        {
@@ -7085,22 +7504,20 @@ mDNSlocal void PowerChanged(void *refcon, io_service_t service, natural_t messag
                                                                                                mDNS_Unlock(m);
                                                                                        
                                                                                                break;
-               case kIOMessageSystemWillRestart:               LogInfo("PowerChanged kIOMessageSystemWillRestart     (no action)");    break;  // E0000310
-               case kIOMessageSystemWillPowerOn:               LogInfo("PowerChanged kIOMessageSystemWillPowerOn");                                                    // E0000320
+               case kIOMessageSystemWillRestart:               LogSPS("PowerChanged kIOMessageSystemWillRestart     (no action)");     break;          // E0000310
+               case kIOMessageSystemWillPowerOn:               LogSPS("PowerChanged kIOMessageSystemWillPowerOn");                                                             // E0000320
 
-                                                                                       #if ! TARGET_OS_EMBEDDED
                                                                                                // On Leopard and earlier, on wake from sleep, instead of reporting link state down, Apple
                                                                                                // Ethernet drivers report "hardware incapable of detecting link state", which the kernel
                                                                                                // interprets as "should assume we have networking", which results in the first 4-5 seconds
                                                                                                // of packets vanishing into a black hole. To work around this, on wake from sleep,
                                                                                                // we block for five seconds to let Ethernet come up, and then resume normal operation.
-                                                                                               if (OSXVers < OSXVers_10_6_SnowLeopard)
+                                                                                               if (OSXVers && OSXVers < OSXVers_10_6_SnowLeopard)
                                                                                                        {
                                                                                                        sleep(5);
                                                                                                        LogMsg("Running on Mac OS X version 10.%d earlier than 10.6; "
                                                                                                                "PowerChanged did sleep(5) to wait for Ethernet hardware", OSXVers - OSXVers_Base);
                                                                                                        }
-                                                                                       #endif
 
                                                                                                // Make sure our interface list is cleared to the empty state, then tell mDNSCore to wake
                                                                                                if (m->SleepState != SleepState_Sleeping)
@@ -7111,7 +7528,7 @@ mDNSlocal void PowerChanged(void *refcon, io_service_t service, natural_t messag
                                                                                                        }
                                                                                                PowerOn(m);
                                                                                                break;
-               default:                                                                LogInfo("PowerChanged unknown message %X", messageType);                                break;
+               default:                                                                LogSPS("PowerChanged unknown message %X", messageType); break;
                }
 
        if (messageType == kIOMessageSystemWillSleep) m->p->SleepCookie = (long)messageArgument;
@@ -7171,6 +7588,773 @@ mDNSlocal void SnowLeopardPowerChanged(void *refcon, IOPMConnection connection,
        }
 #endif
 
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - /etc/hosts support
+#endif
+
+// Implementation Notes
+//
+// As /etc/hosts file can be huge (1000s of entries - when this comment was written, the test file had about
+// 23000 entries with about 4000 duplicates), we can't use a linked list to store these entries. So, we parse
+// them into a hash table. The implementation need to be able to do the following things efficiently
+//
+// 1. Detect duplicates e.g., two entries with "1.2.3.4 foo" 
+// 2. Detect whether /etc/hosts has changed and what has changed since the last read from the disk
+// 3. Ability to support multiple addresses per name e.g., "1.2.3.4 foo, 2.3.4.5 foo". To support this, we
+//    need to be able set the RRSet of a resource record to the first one in the list and also update when
+//    one of them go away. This is needed so that the core thinks that they are all part of the same RRSet and
+//    not a duplicate
+// 4. Don't maintain any local state about any records registered with the core to detect changes to /etc/hosts
+//
+// CFDictionary is not a suitable candidate because it does not support duplicates and even if we use a custom
+// "hash" function to solve this, the others are hard to solve. Hence, we share the hash (AuthHash) implementation
+// of the core layer which does all of the above very efficiently
+
+mDNSexport void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result)
+    {
+    (void)m;  // unused
+       (void)rr;
+       (void)result;
+       if (result == mStatus_MemFree)
+               {
+               LogInfo("FreeEtcHosts: %s", ARDisplayString(m, rr));
+               freeL("etchosts", rr);
+               }
+       }
+
+// Returns true on success and false on failure
+mDNSlocal mDNSBool mDNSMacOSXCreateEtcHostsEntry(mDNS *const m, const domainname *domain, const struct sockaddr *sa, const domainname *cname, char *ifname, AuthHash *auth)
+       {
+       AuthRecord *rr;
+       mDNSu32 slot;
+       mDNSu32 namehash;
+       AuthGroup *ag;
+       mDNSInterfaceID InterfaceID = mDNSInterface_LocalOnly;
+       mDNSu16 rrtype;
+
+       if (!domain)
+               {
+               LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! name NULL");
+               return mDNSfalse;
+               }
+       if (!sa && !cname)
+               {
+               LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! sa and cname both NULL");
+               return mDNSfalse;
+               }
+
+       if (sa && sa->sa_family != AF_INET && sa->sa_family != AF_INET6)
+               {
+               LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! sa with bad family %d", sa->sa_family);
+               return mDNSfalse;
+               }
+       
+
+       if (ifname)
+               {
+               mDNSu32 ifindex = if_nametoindex(ifname);
+               if (!ifindex)
+                       {
+                       LogMsg("mDNSMacOSXCreateEtcHostsEntry: hosts entry %##s with invalid ifname %s", domain->c, ifname);
+                       return mDNSfalse;
+                       }
+               InterfaceID = (mDNSInterfaceID)(uintptr_t)ifindex;
+               }
+
+       if (sa)
+               rrtype = (sa->sa_family == AF_INET ? kDNSType_A : kDNSType_AAAA);
+       else
+               rrtype = kDNSType_CNAME;
+
+       // Check for duplicates. See whether we parsed an entry before like this ?
+       slot = AuthHashSlot(domain);
+       namehash = DomainNameHashValue(domain);
+       ag = AuthGroupForName(auth, slot, namehash, domain);
+       if (ag)
+               {
+               rr = ag->members;
+               while (rr)
+                       {
+                       if (rr->resrec.rrtype == rrtype)
+                               {
+                               if (rrtype == kDNSType_A)
+                                       {
+                                       mDNSv4Addr ip;
+                                       ip.NotAnInteger = ((struct sockaddr_in*)sa)->sin_addr.s_addr;
+                                       if (mDNSSameIPv4Address(rr->resrec.rdata->u.ipv4, ip))
+                                               {
+                                               LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same IPv4 address for name %##s", domain->c);
+                                               return mDNSfalse;
+                                               }
+                                       }
+                               else if (rrtype == kDNSType_AAAA)
+                                       {
+                                       mDNSv6Addr ip6;
+                                       ip6.l[0] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[0];
+                                       ip6.l[1] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[1];
+                                       ip6.l[2] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[2];
+                                       ip6.l[3] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[3];
+                                       if (mDNSSameIPv6Address(rr->resrec.rdata->u.ipv6, ip6))
+                                               {
+                                               LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same IPv6 address for name %##s", domain->c);
+                                               return mDNSfalse;
+                                               }
+                                       }
+                               else if (rrtype == kDNSType_CNAME)
+                                       {
+                                       if (SameDomainName(&rr->resrec.rdata->u.name, cname))
+                                               {
+                                               LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same cname %##s for name %##s", cname->c, domain->c);
+                                               return mDNSfalse;
+                                               }
+                                       }
+                               }
+                       rr = rr->next;
+                       }
+               }
+       rr= mallocL("etchosts", sizeof(*rr));
+       if (rr == NULL) return mDNSfalse;
+       mDNSPlatformMemZero(rr, sizeof(*rr));
+       mDNS_SetupResourceRecord(rr, NULL, InterfaceID, rrtype, 1, kDNSRecordTypeKnownUnique, AuthRecordLocalOnly, FreeEtcHosts, NULL);
+       AssignDomainName(&rr->namestorage, domain);
+
+       if (sa)
+               {
+               rr->resrec.rdlength = sa->sa_family == AF_INET ? sizeof(mDNSv4Addr) : sizeof(mDNSv6Addr);
+               if (sa->sa_family == AF_INET)
+                       rr->resrec.rdata->u.ipv4.NotAnInteger = ((struct sockaddr_in*)sa)->sin_addr.s_addr;
+               else
+                       {
+                       rr->resrec.rdata->u.ipv6.l[0] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[0];
+                       rr->resrec.rdata->u.ipv6.l[1] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[1];
+                       rr->resrec.rdata->u.ipv6.l[2] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[2];
+                       rr->resrec.rdata->u.ipv6.l[3] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[3];
+                       }
+               }
+       else
+               {
+               rr->resrec.rdlength = DomainNameLength(cname);
+               rr->resrec.rdata->u.name.c[0] = 0;
+               AssignDomainName(&rr->resrec.rdata->u.name, cname);
+               }
+       rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
+       SetNewRData(&rr->resrec, mDNSNULL, 0);  // Sets rr->rdatahash for us
+       LogInfo("mDNSMacOSXCreateEtcHostsEntry: Adding resource record %s", ARDisplayString(m, rr));
+       InsertAuthRecord(m, auth, rr);
+       return mDNStrue;
+       }
+
+mDNSlocal int EtcHostsParseOneName(int start, int length, char *buffer, char **name)
+       {
+       int i;
+
+       *name = NULL;
+       for (i = start; i < length; i++)
+               {
+               if (buffer[i] != ' ' && buffer[i] != ',' && buffer[i] != '\t')
+                       {
+                       *name = &buffer[i];
+                       
+                       // Found the start of a name, find the end and null terminate
+                       for (i++; i < length; i++)
+                               {
+                               if (buffer[i] == ' ' || buffer[i] == ',' || buffer[i] == '\t')
+                                       {
+                                       buffer[i] = 0;
+                                       break;
+                                       }
+                               }
+                       return i;
+                       }
+               }
+       return -1;
+       }
+
+mDNSlocal void mDNSMacOSXParseEtcHostsLine(mDNS *const m, char *buffer, ssize_t length, AuthHash *auth)
+       {
+       int i;
+       int ifStart = 0;
+       char *ifname = NULL;
+       domainname name1d;
+       domainname name2d;
+       char *name1;
+       char *name2;
+       int aliasIndex;
+       
+       // Find the end of the address string
+       for (i = 0; i < length; i++)
+               {
+               if (buffer[i] == ' ' || buffer[i] == ',' || buffer[i] == '\t' || buffer[i] == '%')
+                       {
+                       if (buffer[i] == '%')
+                               ifStart = i + 1;
+                       buffer[i] = 0;
+                       break;
+                       }
+               }
+       
+       // Convert the address string to an address
+       struct addrinfo hints;
+       bzero(&hints, sizeof(hints));
+       hints.ai_flags = AI_NUMERICHOST;
+       struct addrinfo *gairesults = NULL;
+       if (getaddrinfo(buffer, NULL, &hints, &gairesults) != 0)
+               {
+               LogInfo("mDNSMacOSXParseEtcHostsLine: getaddrinfo returning null");
+               return;
+               }
+
+       if (ifStart)
+               {
+               // Parse the interface
+               ifname = &buffer[ifStart];
+               for (i = ifStart + 1; i < length; i++)
+                       {
+                       if (buffer[i] == ' ' || buffer[i] == ',' || buffer[i] == '\t')
+                               {
+                               buffer[i] = 0;
+                               break;
+                               }
+                       }
+               }
+               
+       i = EtcHostsParseOneName(i + 1, length, buffer, &name1);
+       if (i == length)
+               {
+               // Common case (no aliases) : The entry is of the form "1.2.3.4 somehost" with no trailing white spaces/tabs etc.
+               if (!MakeDomainNameFromDNSNameString(&name1d, name1))
+                       {
+                       LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name1);
+                       freeaddrinfo(gairesults);
+                       return;
+                       }
+               mDNSMacOSXCreateEtcHostsEntry(m, &name1d, gairesults->ai_addr, mDNSNULL, ifname, auth);
+               }
+       else if (i != -1)
+               {
+               domainname first;
+               // We might have some extra white spaces at the end for the common case of "1.2.3.4 somehost".
+               // When we parse again below, EtchHostsParseOneName would return -1 and we will end up
+               // doing the right thing.
+               if (!MakeDomainNameFromDNSNameString(&first, name1))
+                       {
+                       LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name1);
+                       freeaddrinfo(gairesults);
+                       return;
+                       }
+               // If the /etc/hosts has an entry like this
+               //
+               // 1.2.3.4 sun star bright
+               //
+               // star and bright are aliases (gethostbyname h_alias should point to these) and sun is the canonical
+               // name (getaddrinfo ai_cannonname and gethostbyname h_name points to "sun")
+               //
+               // To achieve this, we need to add the entry like this:
+               //
+               // star CNAME bright
+               // bright CNAME sun
+               // sun A 1.2.3.4
+               //
+               // We store the first name we parsed in "first". Then we parse additional names adding CNAME records
+               // till we reach the end. When we reach the end, we wrap around and add one final CNAME with the last 
+               // entry and the first entry. Finally, we add the Address (A/AAAA) record.
+               aliasIndex = 0;
+               while (i <= length)
+                       {
+                       // Parse a name. If there are no names, we need to know whether we
+                       // parsed CNAMEs before or not. If we parsed CNAMEs before, then we
+                       // add a CNAME with the last name and the first name. Otherwise, this
+                       // is same as the common case above where the line has just one name
+                       // but with trailing white spaces.
+                       i = EtcHostsParseOneName(i + 1, length, buffer, &name2);
+                       if (name2)
+                               {
+                               if (!MakeDomainNameFromDNSNameString(&name2d, name2))
+                                       {
+                                       LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name2);
+                                       freeaddrinfo(gairesults);
+                                       return;
+                                       }
+                               aliasIndex++;
+                               }
+                       else if (!aliasIndex)
+                               {
+                               // We have never parsed any aliases. This case happens if there
+                               // is just one name and some extra white spaces at the end.
+                               LogInfo("mDNSMacOSXParseEtcHostsLine: White space at the end of %##s", first.c);
+                               break;
+                               }
+                       else
+                               {
+                               // We have parsed at least one alias before and we reached the end of the line.
+                               // Setup a CNAME for the last name with "first" name as its RDATA
+                               name2d.c[0] = 0;
+                               AssignDomainName(&name2d, &first);
+                               }
+
+                       // Don't add a CNAME for the first alias we parse (see the example above).
+                       // As we parse more, we might discover that there are no more aliases, in
+                       // which case we would have set "name2d" to "first" above. We need to add
+                       // the CNAME in that case.
+
+                       if (aliasIndex > 1 || SameDomainName(&name2d, &first))
+                               {
+                               // Ignore if it points to itself 
+                               if (!SameDomainName(&name1d, &name2d))
+                                       {
+                                       if (!mDNSMacOSXCreateEtcHostsEntry(m, &name1d, mDNSNULL, &name2d, ifname, auth))
+                                               {
+                                               freeaddrinfo(gairesults);
+                                               return;
+                                               }
+                                       }
+                               else
+                                       LogMsg("mDNSMacOSXParseEtcHostsLine: Ignoring entry with same names name1 %##s, name2 %##s", name1d.c, name2d.c);
+                               }
+
+                       // If we have already wrapped around, we just need to add the A/AAAA record alone
+                       // which is done below
+                       if (SameDomainName(&name2d, &first)) break;
+
+                       // Remember the current name so that we can set the CNAME record if we parse one
+                       // more name
+                       name1d.c[0] = 0;
+                       AssignDomainName(&name1d, &name2d);
+                       }
+               // Added all the CNAMEs if any, add the "A/AAAA" record
+               mDNSMacOSXCreateEtcHostsEntry(m, &first, gairesults->ai_addr, mDNSNULL, ifname, auth);
+               }
+       freeaddrinfo(gairesults);
+       }
+
+mDNSlocal void mDNSMacOSXParseEtcHosts(mDNS *const m, int fd, AuthHash *auth)
+       {
+       if (fd == -1) { LogInfo("mDNSMacOSXParseEtcHosts: fd is -1"); return; }
+       
+       LogInfo("mDNSMacOSXParseEtcHosts: Parsing etc hosts");
+
+       // Read through the file
+       char    readBuf[1024]; // support a maximum line of 1024 bytes including newline
+       off_t   offset = 0;
+       int     comment = 0;
+       ssize_t i = 0;
+       ssize_t length;
+       ssize_t linestart;
+       while ((length = pread(fd, &readBuf[i], sizeof(readBuf) - i, offset) + i) >= i)
+               {
+               if (length == i)
+                       {
+                       // end of file
+                       if (length != 0) mDNSMacOSXParseEtcHostsLine(m, readBuf, length, auth);
+                       break;
+                       }
+               
+               // increment our offset by the number of bytes we read
+               offset += length - i;
+               
+               // loop through the buffer looking for end of lines
+               for (linestart = 0; i < length; i++)
+                       {
+                       if (readBuf[i] == '#' || readBuf[i] == '\r' || readBuf[i] == '\n')
+                               {
+                               int parseline = i - linestart > 0 && comment == 0;
+                               comment = readBuf[i] == '#';
+                               if (parseline)
+                                       {
+                                       readBuf[i] = 0;
+                                       mDNSMacOSXParseEtcHostsLine(m, &readBuf[linestart], i - linestart, auth);
+                                       }
+                               linestart = i + 1;
+                               }
+                       }
+               
+               // prepare to read the next chunk of the file
+               if (linestart == 0)
+                       {
+                       // single line was larger than our buffer, we're going to ignore it
+                       comment = 1; // treat remainder of line as comment
+                       i = 0;
+                       }
+               else
+                       {
+                       i = length - linestart;
+                       if (i) memmove(readBuf, &readBuf[linestart], i);
+                       }
+               }
+       }
+
+mDNSlocal void mDNSMacOSXUpdateEtcHosts(mDNS *const m);
+
+mDNSlocal int mDNSMacOSXGetEtcHostsFD(mDNS *const m)
+       {
+#ifdef __DISPATCH_GROUP__
+       // Can't do this stuff to be notified of changes in /etc/hosts if we don't have libdispatch
+       static dispatch_queue_t         etcq     = 0;
+       static dispatch_source_t        etcsrc   = 0;
+       static dispatch_source_t        hostssrc = 0;
+       
+       // First time through? just schedule ourselves on the main queue and we'll do the work later
+       if (!etcq)
+       {
+               etcq = dispatch_get_main_queue();
+               if (etcq)
+                       {
+                       // Do this work on the queue, not here - solves potential synchronization issues
+                       dispatch_async(etcq, ^{mDNSMacOSXUpdateEtcHosts(m);});
+                       }
+               return -1;
+       }
+       
+       if (hostssrc) return dispatch_source_get_handle(hostssrc);
+#endif
+
+       int fd = open("/etc/hosts", O_RDONLY);
+       
+#ifdef __DISPATCH_GROUP__
+       // Can't do this stuff to be notified of changes in /etc/hosts if we don't have libdispatch
+       if (fd == -1)
+               {
+               // If the open failed and we're already watching /etc, we're done
+               if (etcsrc) { LogInfo("mDNSMacOSXGetEtcHostsFD: Returning etcfd because no etchosts"); return fd; }
+               
+               // we aren't watching /etc, we should be
+               fd = open("/etc", O_RDONLY);
+               if (fd == -1) { LogInfo("mDNSMacOSXGetEtcHostsFD: etc does not exist"); return -1; }
+               etcsrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_RENAME, etcq);
+               if (etcsrc == NULL)
+                       {
+                       close(fd);
+                       return -1;
+                       }
+               dispatch_source_set_event_handler(etcsrc,
+                       ^{
+                       u_int32_t       flags = dispatch_source_get_data(etcsrc);
+                       LogMsg("mDNSMacOSXGetEtcHostsFD: /etc changed 0x%x", flags);
+                       if ((flags & (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_RENAME)) != 0)
+                               {
+                               dispatch_source_cancel(etcsrc);
+                               dispatch_release(etcsrc);
+                               etcsrc = NULL;
+                               dispatch_async(etcq, ^{mDNSMacOSXUpdateEtcHosts(m);});
+                               return;
+                               }
+                       if ((flags & DISPATCH_VNODE_WRITE) != 0 && hostssrc == NULL)
+                               {
+                               mDNSMacOSXUpdateEtcHosts(m);
+                               }
+                       });
+               dispatch_source_set_cancel_handler(etcsrc, ^{close(fd);});
+               dispatch_resume(etcsrc);
+               
+               // Try and open /etc/hosts once more now that we're watching /etc, in case we missed the creation
+               fd = open("/etc/hosts", O_RDONLY | O_EVTONLY);
+               if (fd == -1) { LogMsg("mDNSMacOSXGetEtcHostsFD etc hosts does not exist, watching etc"); return -1; }
+               }
+       
+       // create a dispatch source to watch for changes to hosts file
+       hostssrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd,
+                                       (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_RENAME |
+                                       DISPATCH_VNODE_ATTRIB | DISPATCH_VNODE_EXTEND | DISPATCH_VNODE_LINK | DISPATCH_VNODE_REVOKE), etcq);
+       if (hostssrc == NULL)
+               {
+               close(fd);
+               return -1;
+               }
+       dispatch_source_set_event_handler(hostssrc,
+               ^{
+               u_int32_t       flags = dispatch_source_get_data(hostssrc);
+               LogInfo("mDNSMacOSXGetEtcHostsFD: /etc/hosts changed 0x%x", flags);
+               if ((flags & (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_RENAME)) != 0)
+                       {
+                       dispatch_source_cancel(hostssrc);
+                       dispatch_release(hostssrc);
+                       hostssrc = NULL;
+                       // Bug in LibDispatch: wait a second before scheduling the block. If we schedule
+                       // the block immediately, we try to open the file and the file may not exist and may
+                       // fail to get a notification in the future. When the file does not exist and
+                       // we start to monitor the directory, on "dispatch_resume" of that source, there
+                       // is no guarantee that the file creation will be notified always because when
+                       // the dispatch_resume returns, the kevent manager may not have registered the
+                       // kevent yet but the file may have been created
+                       usleep(1000000);
+                       dispatch_async(etcq, ^{mDNSMacOSXUpdateEtcHosts(m);});
+                       return;
+                       }
+               if ((flags & DISPATCH_VNODE_WRITE) != 0)
+                       {
+                       mDNSMacOSXUpdateEtcHosts(m);
+                       }
+               });
+       dispatch_source_set_cancel_handler(hostssrc, ^{LogInfo("mDNSMacOSXGetEtcHostsFD: Closing etchosts fd %d", fd); close(fd);});
+       dispatch_resume(hostssrc);
+       
+       // Cleanup /etc source, no need to watch it if we already have /etc/hosts
+       if (etcsrc)
+               {
+               dispatch_source_cancel(etcsrc);
+               dispatch_release(etcsrc);
+               etcsrc = NULL;
+               }
+       
+       LogInfo("mDNSMacOSXGetEtcHostsFD: /etc/hosts being monitored, and not etc");
+       return hostssrc ? (int)dispatch_source_get_handle(hostssrc) : -1;
+#else
+       (void)m;
+       return fd;
+#endif;
+       }
+
+// When /etc/hosts is modified, flush all the cache records as there may be local
+// authoritative answers now
+mDNSlocal void FlushAllCacheRecords(mDNS *const m)
+       {
+       CacheRecord *cr;
+       mDNSu32 slot;
+       CacheGroup *cg;
+
+       FORALL_CACHERECORDS(slot, cg, cr)
+               {
+               // Skip multicast.
+               if (cr->resrec.InterfaceID) continue;
+
+               // If a resource record can answer A or AAAA, they need to be flushed so that we will
+               // never used to deliver an ADD or RMV
+               if (RRTypeAnswersQuestionType(&cr->resrec, kDNSType_A) ||
+                       RRTypeAnswersQuestionType(&cr->resrec, kDNSType_AAAA))
+                       {
+                       LogInfo("FlushAllCacheRecords: Purging Resourcerecord %s", CRDisplayString(m, cr));
+                       mDNS_PurgeCacheResourceRecord(m, cr);
+                       }
+               }
+       }
+
+// Add new entries to the core. If justCheck is set, this function does not add, just returns true
+mDNSlocal mDNSBool EtcHostsAddNewEntries(mDNS *const m, AuthHash *newhosts, mDNSBool justCheck)
+       {
+       AuthGroup *ag;
+       mDNSu32 slot;
+       AuthRecord *rr, *primary, *rrnext;
+       for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+               for (ag = newhosts->rrauth_hash[slot]; ag; ag = ag->next)
+                       {
+                       primary = NULL;
+                       for (rr = ag->members; rr; rr = rrnext)
+                               {
+                               rrnext = rr->next;
+                               AuthGroup *ag1;
+                               AuthRecord *rr1;
+                               mDNSBool found = mDNSfalse;
+                               ag1 = AuthGroupForRecord(&m->rrauth, slot, &rr->resrec);
+                               if (ag1 && ag1->members)
+                                       {
+                                       if (!primary) primary = ag1->members;
+                                       rr1 = ag1->members;
+                                       while (rr1)
+                                               {
+                                               // We are not using InterfaceID in checking for duplicates. This means,
+                                               // if there are two addresses for a given name e.g., fe80::1%en0 and
+                                               // fe80::1%en1, we only add the first one. It is not clear whether
+                                               // this is a common case. To fix this, we also need to modify
+                                               // mDNS_Register_internal in how it handles duplicates. If it becomes a
+                                               // common case, we will fix it then.
+                                               if (IdenticalResourceRecord(&rr1->resrec, &rr->resrec))
+                                                       {
+                                                       LogInfo("EtcHostsAddNewEntries: Skipping, not adding %s", ARDisplayString(m, rr1));
+                                                       found = mDNStrue;
+                                                       break;
+                                                       }
+                                               rr1 = rr1->next;
+                                               }
+                                       }
+                               if (!found)
+                                       {
+                                       if (justCheck) 
+                                               {
+                                               LogInfo("EtcHostsAddNewEntries: Entry %s not registered with core yet", ARDisplayString(m, rr));
+                                               return mDNStrue;
+                                               }
+                                       RemoveAuthRecord(m, newhosts, rr);
+                                       // if there is no primary, point to self
+                                       rr->RRSet = (primary ? primary : rr);
+                                       rr->next = NULL;
+                                       LogInfo("EtcHostsAddNewEntries: Adding %s", ARDisplayString(m, rr));
+                                       if (mDNS_Register_internal(m, rr) != mStatus_NoError)
+                                               LogMsg("EtcHostsAddNewEntries: mDNS_Register failed for %s", ARDisplayString(m, rr));
+                                       }
+                               }
+                       }
+               return mDNSfalse;
+       }
+
+// Delete entries from the core that are no longer needed. If justCheck is set, this function
+// does not delete, just returns true
+mDNSlocal mDNSBool EtcHostsDeleteOldEntries(mDNS *const m, AuthHash *newhosts, mDNSBool justCheck)
+       {
+       AuthGroup *ag;
+       mDNSu32 slot;
+       AuthRecord *rr, *primary, *rrnext;
+       for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+               for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next)
+                       for (rr = ag->members; rr; rr = rrnext)
+                               {
+                               mDNSBool found = mDNSfalse;
+                               AuthGroup *ag1;
+                               AuthRecord *rr1;
+                               rrnext = rr->next;
+                               if (rr->RecordCallback != FreeEtcHosts) continue;
+                               ag1 = AuthGroupForRecord(newhosts, slot, &rr->resrec);
+                               if (ag1)
+                                       {
+                                       primary = rr1 = ag1->members;
+                                       while (rr1)
+                                               {
+                                               if (IdenticalResourceRecord(&rr1->resrec, &rr->resrec))
+                                                       {
+                                                       LogInfo("EtcHostsDeleteOldEntries: Old record %s found in new, skipping", ARDisplayString(m, rr));
+                                                       found = mDNStrue;
+                                                       break;
+                                                       }
+                                               rr1 = rr1->next;
+                                               }
+                                       }
+                               // there is no corresponding record in newhosts for the same name. This means
+                               // we should delete this from the core.
+                               if (!found)
+                                       {
+                                       if (justCheck)
+                                               {
+                                               LogInfo("EtcHostsDeleteOldEntries: Record %s not found in new, deleting", ARDisplayString(m, rr));
+                                               return mDNStrue;
+                                               }
+                                       // if primary is going away, make sure that the rest of the records
+                                       // point to the new primary
+                                       if (rr == ag->members)
+                                               {
+                                               AuthRecord *new_primary = rr->next;
+                                               AuthRecord *r = new_primary;
+                                               while (r)
+                                                       {
+                                                       if (r->RRSet == rr)
+                                                               {
+                                                               LogInfo("EtcHostsDeleteOldEntries: Updating Resource Record %s to primary", ARDisplayString(m, r));
+                                                               r->RRSet = new_primary;
+                                                               }
+                                                       else LogMsg("EtcHostsDeleteOldEntries: ERROR!! Resource Record %s not pointing to primary %##s", ARDisplayString(m, r), r->resrec.name);
+                                                       r = r->next;
+                                                       }
+                                               }
+                                       LogInfo("EtcHostsDeleteOldEntries: Deleting %s", ARDisplayString(m, rr));
+                                       mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
+                                       }
+                               }
+       return mDNSfalse;
+       }
+
+mDNSlocal void UpdateEtcHosts(mDNS *const m, void *context)
+       {
+       AuthHash *newhosts = (AuthHash *)context;
+
+       if (!m->mDNS_busy) LogMsg("UpdateEtcHosts: ERROR!! Lock not held");
+
+       //Delete old entries from the core if they are not present in the newhosts
+       EtcHostsDeleteOldEntries(m, newhosts, mDNSfalse);
+       // Add the new entries to the core if not already present in the core
+       EtcHostsAddNewEntries(m, newhosts, mDNSfalse);
+       }
+
+mDNSlocal void FreeNewHosts(AuthHash *newhosts)
+       {
+       mDNSu32 slot;
+       AuthGroup *ag, *agnext;
+       AuthRecord *rr, *rrnext;
+
+       for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+               for (ag = newhosts->rrauth_hash[slot]; ag; ag = agnext)
+                       {
+                       agnext = ag->next;
+                       for (rr = ag->members; rr; rr = rrnext)
+                               {
+                               rrnext = rr->next;
+                               freeL("etchosts", rr);
+                               }
+                       freeL("AuthGroups", ag);
+                       }
+       }
+
+mDNSlocal void mDNSMacOSXUpdateEtcHosts(mDNS *const m)
+       {
+       AuthHash newhosts;
+       
+       // As we will be modifying the core, we can only have one thread running at
+       // any point in time.
+       KQueueLock(m);
+
+       mDNSPlatformMemZero(&newhosts, sizeof(AuthHash));
+
+       // Get the file desecriptor (will trigger us to start watching for changes)
+       int fd = mDNSMacOSXGetEtcHostsFD(m);
+       if (fd != -1)
+               {
+               LogInfo("mDNSMacOSXUpdateEtcHosts: Parsing /etc/hosts fd %d", fd);
+               mDNSMacOSXParseEtcHosts(m, fd, &newhosts);
+               }
+       else LogInfo("mDNSMacOSXUpdateEtcHosts: /etc/hosts is not present");
+       
+       // Optimization: Detect whether /etc/hosts changed or not.
+       //
+       // 1. Check to see if there are any new entries. We do this by seeing whether any entries in 
+       //    newhosts is already registered with core.  If we find at least one entry that is not
+       //    registered with core, then it means we have work to do.
+       //    
+       // 2. Next, we check to see if any of the entries that are registered with core is not present
+       //   in newhosts. If we find at least one entry that is not present, it means we have work to
+       //   do.
+       // 
+       // Note: We may not have to hold the lock right here as KQueueLock is held which prevents any
+       // other thread from running. But mDNS_Lock is needed here as we will be traversing the core
+       // data structure in EtcHostsDeleteOldEntries/NewEntries which might expect the lock to be held
+       // in the future and this code does not have to change.
+       mDNS_Lock(m);
+       // Add the new entries to the core if not already present in the core
+       if (!EtcHostsAddNewEntries(m, &newhosts, mDNStrue))
+               {
+               // No new entries to add, check to see if we need to delete any old entries from the
+               // core if they are not present in the newhosts
+               if (!EtcHostsDeleteOldEntries(m, &newhosts, mDNStrue))
+                       {
+                       LogInfo("mDNSMacOSXUpdateEtcHosts: No work");
+                       mDNS_Unlock(m);
+                       KQueueUnlock(m, "/etc/hosts changed");
+                       FreeNewHosts(&newhosts);
+                       return;
+                       }
+               }
+
+       // This will flush the cache, stop and start the query so that the queries
+       // can look at the /etc/hosts again
+       //
+       // Notes:
+       //
+       // We can't delete and free the records here. We wait for the mDNSCoreRestartAddressQueries to
+       // deliver RMV events. It has to be done in a deferred way because we can't deliver RMV
+       // events for local records *before* the RMV events for cache records. mDNSCoreRestartAddressQueries
+       // delivers these events in the right order and then calls us back to delete them.
+       //
+       // Similarly, we do a deferred Registration of the record because mDNSCoreRestartAddressQueries
+       // is a common function that looks at all local auth records and delivers a RMV including
+       // the records that we might add here. If we deliver a ADD here, it will get a RMV and then when
+       // the query is restarted, it will get another ADD. To avoid this (ADD-RMV-ADD), we defer registering
+       // the record until the RMVs are delivered in mDNSCoreRestartAddressQueries after which UpdateEtcHosts
+       // is called back where we do the Registration of the record. This results in RMV followed by ADD which
+    // looks normal.
+       mDNSCoreRestartAddressQueries(m, mDNSfalse, FlushAllCacheRecords, UpdateEtcHosts, &newhosts);
+       mDNS_Unlock(m);
+
+       KQueueUnlock(m, "/etc/hosts changed");
+       FreeNewHosts(&newhosts);
+       }
+
 #if COMPILER_LIKES_PRAGMA_MARK
 #pragma mark -
 #pragma mark - Initialization & Teardown
@@ -7186,7 +8370,7 @@ CF_EXPORT const CFStringRef _kCFSystemVersionBuildVersionKey;
 // Major version  8 is 10.4.x (Tiger)
 // Major version  9 is 10.5.x (Leopard)
 // Major version 10 is 10.6.x (SnowLeopard)
-mDNSexport int mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring)
+mDNSexport void mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring)
        {
        int major = 0, minor = 0;
        char letter = 0, prodname[256]="<Unknown>", prodvers[256]="<Unknown>", buildver[256]="<Unknown>";
@@ -7204,7 +8388,10 @@ mDNSexport int mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring)
                }
        if (!major) { major=8; LogMsg("Note: No Major Build Version number found; assuming 8"); }
        if (HINFO_SWstring) mDNS_snprintf(HINFO_SWstring, 256, "%s %s (%s), %s", prodname, prodvers, buildver, STRINGIFY(mDNSResponderVersion));
-       return(major);
+       //LogMsg("%s %s (%s), %d %c %d", prodname, prodvers, buildver, major, letter, minor);
+       
+       // If product name is "Mac OS X" (or similar) we set OSXVers, else we set iOSVers;
+       if ((prodname[0] & 0xDF) == 'M') OSXVers = major; else iOSVers = major;
        }
 
 // Test to see if we're the first client running on UDP port 5353, by trying to bind to 5353 without using SO_REUSEPORT.
@@ -7247,7 +8434,7 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m)
        m->p->CFRunLoop = CFRunLoopGetCurrent();
 
        char HINFO_SWstring[256] = "";
-       OSXVers = mDNSMacOSXSystemBuildNumber(HINFO_SWstring);
+       mDNSMacOSXSystemBuildNumber(HINFO_SWstring);
 
        // In 10.4, mDNSResponder is launched very early in the boot process, while other subsystems are still in the process of starting up.
        // If we can't read the user's preferences, then we sleep a bit and try again, for up to five seconds before we give up.
@@ -7273,11 +8460,13 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m)
 //     if (sysctl(get_model, 2, HINFO_HWstring_buffer, &len_model, NULL, 0) == 0 && strchr(HINFO_HWstring_buffer, ','))
        if (sysctl(get_model, 2, HINFO_HWstring_buffer, &len_model, NULL, 0) == 0)
                HINFO_HWstring = HINFO_HWstring_buffer;
-       HINFO_HWstring_prefixlen = strcspn(HINFO_HWstring, "0123456789");
 
-       if (OSXVers <  OSXVers_10_3_Panther     ) m->KnownBugs |= mDNS_KnownBug_PhantomInterfaces;
-       if (OSXVers <= OSXVers_10_6_SnowLeopard ) m->KnownBugs |= mDNS_KnownBug_LimitedIPv6;
-       if (OSXVers >= OSXVers_10_6_SnowLeopard ) m->KnownBugs |= mDNS_KnownBug_LossySyslog;
+       // For names of the form "iPhone2,1" we use "iPhone" as the prefix for automatic name generation.
+       // For names of the form "N88AP" containg no comma, we use the entire string.
+       HINFO_HWstring_prefixlen = strchr(HINFO_HWstring_buffer, ',') ? strcspn(HINFO_HWstring, "0123456789") : strlen(HINFO_HWstring);
+
+       if (OSXVers && OSXVers <= OSXVers_10_6_SnowLeopard) m->KnownBugs |= mDNS_KnownBug_LimitedIPv6;
+       if (OSXVers && OSXVers >= OSXVers_10_6_SnowLeopard) m->KnownBugs |= mDNS_KnownBug_LossySyslog;
        if (mDNSPlatformInit_CanReceiveUnicast()) m->CanReceiveUnicastOn5353 = mDNStrue;
 
        mDNSu32 hlen = mDNSPlatformStrLen(HINFO_HWstring);
@@ -7330,7 +8519,7 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m)
        m->p->prevoldhostlabel.c[0] = 0;
        m->p->prevnewhostlabel.c[0] = 0;
        m->p->NotifyUser         = 0;
-       m->p->KeyChainBugTimer   = 0;
+       m->p->KeyChainTimer      = 0;
        m->p->WakeAtUTC          = 0;
        m->p->RequestReSleep     = 0;
        
@@ -7405,11 +8594,11 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m)
                if (!m->p->PowerConnection) { LogMsg("mDNSPlatformInit_setup: IORegisterForSystemPower failed"); return(-1); }
                else
                        {
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
                        IONotificationPortSetDispatchQueue(m->p->PowerPortRef, dispatch_get_main_queue());
 #else
                        CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(m->p->PowerPortRef), kCFRunLoopDefaultMode);
-#endif /* __LIB_DISPATCH__ */
+#endif /* MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM */
                        }
                }
 
@@ -7427,6 +8616,7 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m)
        else if (!strncasecmp(HINFO_HWstring, "TimeCapsule", 11)) { SPMetricPortability = 34 /*  4kg */; SPMetricMarginalPower = 10 /*  ~0W */; SPMetricTotalPower = 70 /*  13W */; }
        else if (!strncasecmp(HINFO_HWstring, "AirPort",      7)) { SPMetricPortability = 35 /*  3kg */; SPMetricMarginalPower = 10 /*  ~0W */; SPMetricTotalPower = 70 /*  12W */; }
        else if (!strncasecmp(HINFO_HWstring, "AppleTV",      7)) { SPMetricPortability = 35 /*  3kg */; SPMetricMarginalPower = 10 /*  ~0W */; SPMetricTotalPower = 73 /*  20W */; }
+       else if (!strncasecmp(HINFO_HWstring, "K66AP",        5)) { SPMetricPortability = 35 /*  3kg */; SPMetricMarginalPower = 60 /*   1W */; SPMetricTotalPower = 63 /*   2W */; }
        else if (!strncasecmp(HINFO_HWstring, "MacBook",      7)) { SPMetricPortability = 37 /*  2kg */; SPMetricMarginalPower = 71 /*  13W */; SPMetricTotalPower = 72 /*  15W */; }
        else if (!strncasecmp(HINFO_HWstring, "PowerBook",    9)) { SPMetricPortability = 37 /*  2kg */; SPMetricMarginalPower = 71 /*  13W */; SPMetricTotalPower = 72 /*  15W */; }
        LogSPS("HW_MODEL: %.*s (%s) Portability %d Marginal Power %d Total Power %d",
@@ -7438,7 +8628,7 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m)
        m->p->ConndBTMMDict = mDNSNULL;
 #endif // APPLE_OSX_mDNSResponder
 
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
        // Currently this is not defined. SSL code will eventually fix this. If it becomes
        // critical, we will define this to workaround the bug in SSL.
 #ifdef __SSL_NEEDS_SERIALIZATION__
@@ -7448,6 +8638,7 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m)
 #endif
        if (SSLqueue == mDNSNULL) LogMsg("dispatch_queue_create: SSL queue NULL");
 #endif
+       mDNSMacOSXUpdateEtcHosts(m);
        return(mStatus_NoError);
        }
 
@@ -7492,7 +8683,7 @@ mDNSexport void mDNSPlatformClose(mDNS *const m)
        {
        if (m->p->PowerConnection)
                {
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
                IONotificationPortSetDispatchQueue(m->p->PowerPortRef, NULL);           
 #else
                CFRunLoopRemoveSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(m->p->PowerPortRef), kCFRunLoopDefaultMode);
@@ -7507,7 +8698,7 @@ mDNSexport void mDNSPlatformClose(mDNS *const m)
 
        if (m->p->Store)
                {
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
                if (!SCDynamicStoreSetDispatchQueue(m->p->Store, NULL))
                        LogMsg("mDNSPlatformClose: SCDynamicStoreSetDispatchQueue failed");
 #else
@@ -7562,7 +8753,7 @@ mDNSexport void mDNSPlatformClose(mDNS *const m)
                {
                AnonymousRacoonConfig = mDNSNULL;
                LogInfo("mDNSPlatformClose: Deconfiguring autotunnel");
-               (void)mDNSConfigureServer(kmDNSDown, mDNSNULL);
+               (void)mDNSConfigureServer(kmDNSDown, mDNSNULL, mDNSNULL);
                }
 
        if (m->AutoTunnelHostAddrActive && m->AutoTunnelHostAddr.b[0])
@@ -7627,14 +8818,12 @@ mDNSexport mDNSs32 mDNSPlatformRawTime(void)
                LogMsg("mDNSPlatformRawTime: this_mach_absolute_time %08X%08X", this_mach_absolute_time);
                // Update last_mach_absolute_time *before* calling NotifyOfElusiveBug()
                last_mach_absolute_time = this_mach_absolute_time;
-               // Only show "mach_absolute_time went backwards" notice on 10.4 (build 8xyyy) or later.
-               // (This bug happens all the time on 10.3, and we know that's not going to be fixed.)
-               if (OSXVers >= OSXVers_10_4_Tiger)
-                       NotifyOfElusiveBug("mach_absolute_time went backwards!",
-                               "This error occurs from time to time, often on newly released hardware, "
-                               "and usually the exact cause is different in each instance.\r\r"
-                               "Please file a new Radar bug report with the title “mach_absolute_time went backwards” "
-                               "and assign it to Radar Component “Kernel” Version “X”.");
+               // Note: This bug happens all the time on 10.3
+               NotifyOfElusiveBug("mach_absolute_time went backwards!",
+                       "This error occurs from time to time, often on newly released hardware, "
+                       "and usually the exact cause is different in each instance.\r\r"
+                       "Please file a new Radar bug report with the title “mach_absolute_time went backwards” "
+                       "and assign it to Radar Component “Kernel” Version “X”.");
                }
        last_mach_absolute_time = this_mach_absolute_time;
 
@@ -7659,7 +8848,7 @@ mDNSexport void *   mDNSPlatformMemAllocate(mDNSu32 len) { return(mallocL("mDNSP
 #endif
 mDNSexport void     mDNSPlatformMemFree    (void *mem)   { freeL("mDNSPlatformMemFree", mem); }
 
-mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep)
+mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason)
        {
        if (allowSleep && m->p->IOPMAssertion)
                {
@@ -7669,8 +8858,12 @@ mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep)
                }
        else if (!allowSleep && m->p->IOPMAssertion == 0)
                {
-               IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, CFSTR("mDNSResponder"), &m->p->IOPMAssertion);
+#ifdef kIOPMAssertionTypeNoIdleSleep
+               CFStringRef assertionName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s.%d %s"), getprogname(), getpid(), reason ? reason : "");
+               IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, assertionName ? assertionName : CFSTR("mDNSResponder"), &m->p->IOPMAssertion);
+               if (assertionName) CFRelease(assertionName);
                LogInfo("%s Creating NoIdleSleep power assertion", __FUNCTION__);
+#endif
                }
        }
 
@@ -7679,7 +8872,7 @@ mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID Inte
        mDNSu32 ifindex;
 
        // Sanity check
-       ifindex = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID);
+       ifindex = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNStrue);
        if (ifindex <= 0)
                {
                LogMsg("mDNSPlatformSendWakeupPacket: ERROR!! Invalid InterfaceID %u", ifindex);
@@ -7687,3 +8880,15 @@ mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID Inte
                }
        mDNSSendWakeupPacket(ifindex, EthAddr, IPAddr, iteration);
        }
+
+// Called for rr->InterfaceID == mDNSInterface_Any.  
+// If current interface is P2P, verify that record is marked to IncludeP2P.
+mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf)
+       {
+       mDNSBool p2pInterface = (strncmp(intf->ifname, "p2p", 3) == 0);
+
+       if (!p2pInterface || (rr->ARType == AuthRecordAnyIncludeP2P))
+               return mDNStrue;
+       else
+               return mDNSfalse;
+       }
index 522565e75efa60d2c2378b28111db2b021fe1cf7..c914415f6d2082050e7d20d677929c994e3d45c0 100644 (file)
@@ -30,8 +30,8 @@
 #include <netinet/in.h>
 #include "mDNSEmbeddedAPI.h"  // for domain name structure
 
-//#define __LIB_DISPATCH__
-#ifdef __LIB_DISPATCH__
+//#define MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 #include <dispatch/dispatch.h>
 #endif
 
@@ -43,7 +43,7 @@ typedef struct
        KQueueEventCallback      KQcallback;
        void                *KQcontext;
        const char const    *KQtask;            // For debugging messages
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
        dispatch_source_t readSource;
        dispatch_source_t writeSource;
        mDNSBool                  fdClosed;
@@ -91,7 +91,7 @@ struct NetworkInterfaceInfoOSX_struct
        int                      BPF_fd;                        // -1 uninitialized; -2 requested BPF; -3 failed
        int                      BPF_mcfd;                      // Socket for our IPv6 ND group membership
        u_int                    BPF_len;
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
        dispatch_source_t                BPF_source;
 #else
        CFSocketRef              BPF_cfs;
@@ -116,13 +116,7 @@ struct mDNS_PlatformSupport_struct
        mDNSs32                  NotifyUser;
        mDNSs32                  HostNameConflict;      // Time we experienced conflict on our link-local host name
        mDNSs32                  NetworkChanged;
-       
-       // KeyChain frequently fails to notify clients of change events. To work around this
-       // we set a timer and periodically poll to detect if any changes have occurred.
-       // Without this Back To My Mac just does't work for a large number of users.
-       // See <rdar://problem/5124399> Not getting Keychain Changed events when enabling BTMM
-       mDNSs32                  KeyChainBugTimer;
-       mDNSs32                  KeyChainBugInterval;
+       mDNSs32                  KeyChainTimer;
 
        CFRunLoopRef             CFRunLoop;
        SCDynamicStoreRef        Store;
@@ -141,7 +135,7 @@ struct mDNS_PlatformSupport_struct
        long                     SleepCookie;           // Cookie we need to pass to IOAllowPowerChange()
        long                     WakeAtUTC;
        mDNSs32                  RequestReSleep;
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
        dispatch_source_t                timer;
        dispatch_source_t                custom;
 #else
@@ -156,8 +150,9 @@ struct mDNS_PlatformSupport_struct
 
 extern int OfferSleepProxyService;
 extern int DisableSleepProxyClient;
+extern int UseInternalSleepProxy;
+extern int OSXVers, iOSVers;
 extern mDNSBool DisableInboundRelayConnection;
-extern int OSXVers;
 #define OSXVers_Base              4
 #define OSXVers_10_0_Cheetah      4
 #define OSXVers_10_1_Puma         5
@@ -172,10 +167,10 @@ extern int KQueueFD;
 extern void NotifyOfElusiveBug(const char *title, const char *msg);    // Both strings are UTF-8 text
 extern void SetDomainSecrets(mDNS *m);
 extern void mDNSMacOSXNetworkChanged(mDNS *const m);
-extern int mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring);
+extern void mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring);
 extern NetworkInterfaceInfoOSX *IfindexToInterfaceInfoOSX(const mDNS *const m, mDNSInterfaceID ifindex);
 
-#ifdef __LIB_DISPATCH__
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 extern int KQueueSet(int fd, u_short flags, short filter, KQueueEntry *const entryRef);
 mDNSexport void TriggerEventCompletion(void);
 #else
index 323ba3dbbb0c1661ba18581218723c8b6964e732..4a51b875bb8e7858ef88ace59b7c8250ccc6b0f2 100644 (file)
@@ -88,6 +88,8 @@
 (allow file-read-data                 (regex #"^/dev/random$"))
 (allow file-read-data file-write-data (regex #"^/dev/console$"))               ; Needed for syslog early in the boot process
 (allow file-read-data                 (regex #"^/dev/autofs_nowait$")) ; Used by CF to circumvent automount triggers
+(allow file-read-data                 (regex #"^/private/etc/hosts$"))                 ; /etc/hosts support
+(allow file-read-data                 (regex #"^/private/etc$"))                               ; /etc/hosts support
 
 ; Allow us to read and write our socket
 (allow file-read*     file-write*     (regex #"^/private/var/run/mDNSResponder$"))
 ; CRL Cache for SSL/TLS connections
 (allow file-read-data                 (regex #"^/private/var/db/crls/crlcache\.db$"))
 
-; For mDNS sleep proxy offload
-(allow iokit-open (iokit-user-client-class "NVEthernetUserClientMDNS"))
-(allow iokit-open (iokit-user-client-class "mDNSOffloadUserClient"))
-
-; For IOPMConnectionCreate
-(allow iokit-open (iokit-user-client-class "RootDomainUserClient"))
+; For mDNS sleep proxy offload and IOPMConnectionCreate
+(if (defined? 'iokit-open)
+   (begin
+     (allow iokit-open (iokit-user-client-class "NVEthernetUserClientMDNS"))
+     (allow iokit-open (iokit-user-client-class "mDNSOffloadUserClient"))
+     (allow iokit-open (iokit-user-client-class "RootDomainUserClient"))))
 
 ; For D2D
 (allow file-read-data (regex #"^/System/Library/PrivateFrameworks/DeviceToDeviceManager.framework(/|$)"))
index 88c0d940ab76657da3b186fe2a869fc266f466e9..d03b50d65f850eff7d4f6c1df1c4d5e5f7d208a2 100644 (file)
@@ -19,6 +19,7 @@
                                D284BF300ADD81630027CCDF /* PBXTargetDependency */,
                                D284BF260ADD814F0027CCDF /* PBXTargetDependency */,
                                D284BF2A0ADD81530027CCDF /* PBXTargetDependency */,
+                               216EF8EC120295AA004917D2 /* PBXTargetDependency */,
                        );
                        name = "Build More";
                        productName = "Build All";
                        name = "Build Some";
                        productName = "Build Some";
                };
-               FFA572650AF190F10055A0F1 /* SystemLibraries */ = {
+               2141DCF8123FFB5D0086D23E /* SystemLibraries */ = {
                        isa = PBXAggregateTarget;
-                       buildConfigurationList = FFA5726E0AF191200055A0F1 /* Build configuration list for PBXAggregateTarget "SystemLibraries" */;
+                       buildConfigurationList = 2141DD08123FFB830086D23E /* Build configuration list for PBXAggregateTarget "SystemLibraries" */;
                        buildPhases = (
                        );
+                       dependencies = (
+                               2141DD0E123FFC960086D23E /* PBXTargetDependency */,
+                               2130257112400E9300AC839F /* PBXTargetDependency */,
+                       );
+                       name = SystemLibraries;
+                       productName = SystemLibraries;
+               };
+               2141DD0B123FFC7F0086D23E /* SystemLibrariesStatic */ = {
+                       isa = PBXAggregateTarget;
+                       buildConfigurationList = 2141DD18123FFC990086D23E /* Build configuration list for PBXAggregateTarget "SystemLibrariesStatic" */;
+                       buildPhases = (
+                       );
+                       dependencies = (
+                               215FFB1D124002CC00470DE1 /* PBXTargetDependency */,
+                               215FFB1B124002C700470DE1 /* PBXTargetDependency */,
+                               215FFB19124002C100470DE1 /* PBXTargetDependency */,
+                       );
+                       name = SystemLibrariesStatic;
+                       productName = SystemLibrariesStatic;
+               };
+               FFA572650AF190F10055A0F1 /* SystemLibrariesDynamic */ = {
+                       isa = PBXAggregateTarget;
+                       buildConfigurationList = FFA5726E0AF191200055A0F1 /* Build configuration list for PBXAggregateTarget "SystemLibrariesDynamic" */;
+                       buildPhases = (
+                               1F7B473C12B82BFD00868AEF /* ShellScript */,
+                       );
                        dependencies = (
                                FFA572690AF190FF0055A0F1 /* PBXTargetDependency */,
                                FFA5726B0AF191010055A0F1 /* PBXTargetDependency */,
                                FFA5726D0AF191020055A0F1 /* PBXTargetDependency */,
                        );
-                       name = SystemLibraries;
+                       name = SystemLibrariesDynamic;
                        productName = SystemLibraries;
                };
                FFB7657B0AEED96B00583A2C /* Build All */ = {
@@ -58,7 +85,7 @@
                        );
                        dependencies = (
                                FFB7657D0AEED97F00583A2C /* PBXTargetDependency */,
-                               FFA572710AF191230055A0F1 /* PBXTargetDependency */,
+                               2141DCFD123FFB7D0086D23E /* PBXTargetDependency */,
                        );
                        name = "Build All";
                        productName = "Build All";
 /* End PBXAggregateTarget section */
 
 /* Begin PBXBuildFile section */
+               213FB23C12028C4A002B3A08 /* BonjourEvents.c in Sources */ = {isa = PBXBuildFile; fileRef = 213FB22C12028B53002B3A08 /* BonjourEvents.c */; };
+               213FB23D12028C5A002B3A08 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09AB6884FE841BABC02AAC07 /* CoreFoundation.framework */; };
+               215FFAEE124000F900470DE1 /* dnssd_ipc.c in Sources */ = {isa = PBXBuildFile; fileRef = F5E11B5A04A28126019798ED /* dnssd_ipc.c */; };
+               215FFAEF124000F900470DE1 /* dnssd_clientlib.c in Sources */ = {isa = PBXBuildFile; fileRef = FFFA38620AEEDB090065B80A /* dnssd_clientlib.c */; };
+               215FFAF0124000F900470DE1 /* dnssd_clientstub.c in Sources */ = {isa = PBXBuildFile; fileRef = FFFA38640AEEDB130065B80A /* dnssd_clientstub.c */; };
+               215FFAF1124000F900470DE1 /* DNSServiceDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */; };
+               215FFAF2124000F900470DE1 /* DNSServiceDiscoveryRequest.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC01022EAFBA00000109 /* DNSServiceDiscoveryRequest.defs */; };
+               215FFAF3124000F900470DE1 /* DNSServiceDiscoveryReply.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC00022EAFBA00000109 /* DNSServiceDiscoveryReply.defs */; settings = {ATTRIBUTES = (Server, ); }; };
+               215FFAF41240011800470DE1 /* dnssd_ipc.c in Sources */ = {isa = PBXBuildFile; fileRef = F5E11B5A04A28126019798ED /* dnssd_ipc.c */; };
+               215FFAF51240011800470DE1 /* dnssd_clientlib.c in Sources */ = {isa = PBXBuildFile; fileRef = FFFA38620AEEDB090065B80A /* dnssd_clientlib.c */; };
+               215FFAF61240011800470DE1 /* dnssd_clientstub.c in Sources */ = {isa = PBXBuildFile; fileRef = FFFA38640AEEDB130065B80A /* dnssd_clientstub.c */; };
+               215FFAF71240011800470DE1 /* DNSServiceDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */; };
+               215FFAF81240011800470DE1 /* DNSServiceDiscoveryRequest.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC01022EAFBA00000109 /* DNSServiceDiscoveryRequest.defs */; };
+               215FFAF91240011800470DE1 /* DNSServiceDiscoveryReply.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC00022EAFBA00000109 /* DNSServiceDiscoveryReply.defs */; settings = {ATTRIBUTES = (Server, ); }; };
+               215FFAFA1240013400470DE1 /* dnssd_ipc.c in Sources */ = {isa = PBXBuildFile; fileRef = F5E11B5A04A28126019798ED /* dnssd_ipc.c */; };
+               215FFAFB1240013400470DE1 /* dnssd_clientlib.c in Sources */ = {isa = PBXBuildFile; fileRef = FFFA38620AEEDB090065B80A /* dnssd_clientlib.c */; };
+               215FFAFC1240013400470DE1 /* dnssd_clientstub.c in Sources */ = {isa = PBXBuildFile; fileRef = FFFA38640AEEDB130065B80A /* dnssd_clientstub.c */; };
+               215FFAFD1240013400470DE1 /* DNSServiceDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */; };
+               215FFAFE1240013400470DE1 /* DNSServiceDiscoveryRequest.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC01022EAFBA00000109 /* DNSServiceDiscoveryRequest.defs */; };
+               215FFAFF1240013400470DE1 /* DNSServiceDiscoveryReply.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC00022EAFBA00000109 /* DNSServiceDiscoveryReply.defs */; settings = {ATTRIBUTES = (Server, ); }; };
                2E0405F50C3195F700F13B59 /* helper.c in Sources */ = {isa = PBXBuildFile; fileRef = 2E0405F40C3195F700F13B59 /* helper.c */; };
                2E0405F60C31961100F13B59 /* helpermsg.defs in Sources */ = {isa = PBXBuildFile; fileRef = 2E0405EB0C3190DC00F13B59 /* helpermsg.defs */; settings = {ATTRIBUTES = (Server, Client, ); COMPILER_FLAGS = "-Wno-error"; }; };
                2E0406150C3197CB00F13B59 /* libbsm.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 2E0406140C3197CB00F13B59 /* libbsm.dylib */; };
                4AE9B0930F3A52A20080B59D /* safe_vproc.c in Sources */ = {isa = PBXBuildFile; fileRef = 4AE9B0480F39448B0080B59D /* safe_vproc.c */; };
                4AE9B0940F3A52AE0080B59D /* safe_vproc.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AE9B0490F39448B0080B59D /* safe_vproc.h */; };
                4AE9B0950F3A52AE0080B59D /* safe_vproc.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AE9B0490F39448B0080B59D /* safe_vproc.h */; };
+               4BD2B63A134FE09F002B96D5 /* P2PPacketFilter.c in Sources */ = {isa = PBXBuildFile; fileRef = 4BD2B638134FE09F002B96D5 /* P2PPacketFilter.c */; };
+               4BD2B63B134FE09F002B96D5 /* P2PPacketFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BD2B639134FE09F002B96D5 /* P2PPacketFilter.h */; };
                D284BE530ADD80740027CCDF /* DNSServiceDiscoveryDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 6575FBFF022EAFBA00000109 /* DNSServiceDiscoveryDefines.h */; };
                D284BE540ADD80740027CCDF /* dnssd_ipc.h in Headers */ = {isa = PBXBuildFile; fileRef = F5E11B5B04A28126019798ED /* dnssd_ipc.h */; };
                D284BE560ADD80740027CCDF /* DNSServiceDiscoveryReply.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC00022EAFBA00000109 /* DNSServiceDiscoveryReply.defs */; settings = {ATTRIBUTES = (Client, ); }; };
                FFA572490AF18FCC0055A0F1 /* DNSServiceDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */; };
                FFA5724A0AF18FCC0055A0F1 /* DNSServiceDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */; };
                FFA5724B0AF18FCC0055A0F1 /* DNSServiceDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */; };
-               FFA572610AF190940055A0F1 /* DNSServiceDiscovery.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = FFA572600AF1908D0055A0F1 /* DNSServiceDiscovery.h */; };
                FFAE66F0105F0CD900162116 /* ddnswriteconfig in Resources */ = {isa = PBXBuildFile; fileRef = D284BEE80ADD80A70027CCDF /* ddnswriteconfig */; };
                FFB437150EB165BD00E17C68 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 00CA213D02786FC30CCA2C71 /* IOKit.framework */; };
                FFC22AA20B00F42A00BAB070 /* DNSServiceDiscoveryRequest.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC01022EAFBA00000109 /* DNSServiceDiscoveryRequest.defs */; };
                        remoteGlobalIDString = 03067D640C83A3700022BE1F;
                        remoteInfo = "Build Some";
                };
+               2130257012400E9300AC839F /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = FFA572650AF190F10055A0F1;
+                       remoteInfo = SystemLibrariesDynamic;
+               };
+               2141DCFC123FFB7D0086D23E /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 2141DCF8123FFB5D0086D23E;
+                       remoteInfo = SystemLibraries;
+               };
+               2141DD0D123FFC960086D23E /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 2141DD0B123FFC7F0086D23E;
+                       remoteInfo = SystemLibrariesStatic;
+               };
+               215FFB18124002C100470DE1 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 2141DD1C123FFCDB0086D23E;
+                       remoteInfo = libdns_sd_static;
+               };
+               215FFB1A124002C700470DE1 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 2141DD23123FFD0F0086D23E;
+                       remoteInfo = libdns_sd_debug_static;
+               };
+               215FFB1C124002CC00470DE1 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 2141DD29123FFD2C0086D23E;
+                       remoteInfo = libdns_sd_profile_static;
+               };
+               216EF8EB120295AA004917D2 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 213FB21712028A7A002B3A08;
+                       remoteInfo = BonjourEvents;
+               };
                4AE471690EAFF83800A6C5AD /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
                        remoteGlobalIDString = FFA5723C0AF18F450055A0F1;
                        remoteInfo = "libdns_sd profile";
                };
-               FFA572700AF191230055A0F1 /* PBXContainerItemProxy */ = {
-                       isa = PBXContainerItemProxy;
-                       containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
-                       proxyType = 1;
-                       remoteGlobalIDString = FFA572650AF190F10055A0F1;
-                       remoteInfo = SystemLibraries;
-               };
                FFAE66F8105F0CF100162116 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                };
-               FFA572500AF190070055A0F1 /* CopyFiles */ = {
-                       isa = PBXCopyFilesBuildPhase;
-                       buildActionMask = 8;
-                       dstPath = /usr/include/DNSServiceDiscovery;
-                       dstSubfolderSpec = 0;
-                       files = (
-                               FFA572610AF190940055A0F1 /* DNSServiceDiscovery.h in CopyFiles */,
-                       );
-                       runOnlyForDeploymentPostprocessing = 1;
-               };
                FFFF8F770C32F0FD00722979 /* CopyFiles */ = {
                        isa = PBXCopyFilesBuildPhase;
                        buildActionMask = 8;
                000753D303367C1C0CCA2C71 /* mDNSMacOSX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mDNSMacOSX.h; sourceTree = "<group>"; };
                00CA213D02786FC30CCA2C71 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = "<absolute>"; };
                09AB6884FE841BABC02AAC07 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = "<absolute>"; };
+               213FB21812028A7A002B3A08 /* BonjourEvents.plugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BonjourEvents.plugin; sourceTree = BUILT_PRODUCTS_DIR; };
+               213FB22C12028B53002B3A08 /* BonjourEvents.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = BonjourEvents.c; sourceTree = "<group>"; };
+               213FB22D12028B53002B3A08 /* BonjourEvents-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "BonjourEvents-Info.plist"; sourceTree = "<group>"; };
+               2141DD1D123FFCDB0086D23E /* libdns_sd.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdns_sd.a; sourceTree = BUILT_PRODUCTS_DIR; };
+               2141DD24123FFD0F0086D23E /* libdns_sd_debug.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdns_sd_debug.a; sourceTree = BUILT_PRODUCTS_DIR; };
+               2141DD2A123FFD2C0086D23E /* libdns_sd_profile.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdns_sd_profile.a; sourceTree = BUILT_PRODUCTS_DIR; };
                21F432971134AA6800581B69 /* WebFilterDNS.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebFilterDNS.framework; path = /System/Library/PrivateFrameworks/WebFilterDNS.framework; sourceTree = "<absolute>"; };
                2E0405EB0C3190DC00F13B59 /* helpermsg.defs */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.mig; path = helpermsg.defs; sourceTree = "<group>"; };
                2E0405F00C31955500F13B59 /* mDNSResponderHelper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mDNSResponderHelper; sourceTree = BUILT_PRODUCTS_DIR; };
                4ADB5F230F6AB9F400B95BF3 /* helper-entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "helper-entitlements.plist"; sourceTree = "<group>"; };
                4AE9B0480F39448B0080B59D /* safe_vproc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = safe_vproc.c; sourceTree = "<group>"; };
                4AE9B0490F39448B0080B59D /* safe_vproc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = safe_vproc.h; sourceTree = "<group>"; };
+               4BD2B638134FE09F002B96D5 /* P2PPacketFilter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = P2PPacketFilter.c; sourceTree = "<group>"; };
+               4BD2B639134FE09F002B96D5 /* P2PPacketFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = P2PPacketFilter.h; sourceTree = "<group>"; };
                654BE64F02B63B93000001D1 /* mDNSEmbeddedAPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mDNSEmbeddedAPI.h; path = ../mDNSCore/mDNSEmbeddedAPI.h; sourceTree = "<group>"; };
                654BE65002B63B93000001D1 /* mDNSDebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mDNSDebug.h; path = ../mDNSCore/mDNSDebug.h; sourceTree = "<group>"; };
                65713D46025A293200000109 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = /System/Library/Frameworks/SystemConfiguration.framework; sourceTree = "<absolute>"; };
                FF485D5105632E0000130380 /* mDNSResponder.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = mDNSResponder.8; path = ../mDNSShared/mDNSResponder.8; sourceTree = SOURCE_ROOT; };
                FF5852100DD27BD300862BDF /* ClientCommon.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = ClientCommon.c; path = ../Clients/ClientCommon.c; sourceTree = SOURCE_ROOT; };
                FF85880B0BD599F40080D89F /* mDNSResponder.sb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = mDNSResponder.sb; sourceTree = SOURCE_ROOT; };
-               FFA572390AF18F1C0055A0F1 /* libdns_sd_debug.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdns_sd_debug.a; sourceTree = BUILT_PRODUCTS_DIR; };
-               FFA572450AF18F450055A0F1 /* libdns_sd_profile.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdns_sd_profile.a; sourceTree = BUILT_PRODUCTS_DIR; };
+               FFA572390AF18F1C0055A0F1 /* libsystem_dnssd_debug.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libsystem_dnssd_debug.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
+               FFA572450AF18F450055A0F1 /* libsystem_dnssd_profile.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libsystem_dnssd_profile.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
                FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = DNSServiceDiscovery.c; sourceTree = "<group>"; };
                FFA572600AF1908D0055A0F1 /* DNSServiceDiscovery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNSServiceDiscovery.h; sourceTree = "<group>"; };
                FFA572630AF190C20055A0F1 /* dns_sd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dns_sd.h; path = ../mDNSShared/dns_sd.h; sourceTree = SOURCE_ROOT; };
-               FFB765840AEED9C700583A2C /* libdns_sd.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdns_sd.a; sourceTree = BUILT_PRODUCTS_DIR; };
+               FFB765840AEED9C700583A2C /* libsystem_dnssd.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libsystem_dnssd.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
                FFCB6D73075D539900B8AF62 /* PlatformCommon.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = PlatformCommon.c; path = ../mDNSShared/PlatformCommon.c; sourceTree = SOURCE_ROOT; };
                FFE6935007C2CA7F00283007 /* ConfigurationAuthority.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ConfigurationAuthority.h; path = PreferencePane/ConfigurationAuthority.h; sourceTree = SOURCE_ROOT; };
                FFE6935207C2CAA400283007 /* DNSServiceDiscoveryPref.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DNSServiceDiscoveryPref.h; path = PreferencePane/DNSServiceDiscoveryPref.h; sourceTree = SOURCE_ROOT; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
+               213FB21612028A7A002B3A08 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               213FB23D12028C5A002B3A08 /* CoreFoundation.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               2141DD1B123FFCDB0086D23E /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               2141DD22123FFD0F0086D23E /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               2141DD28123FFD2C0086D23E /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                2E0405EE0C31955500F13B59 /* Frameworks */ = {
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                        children = (
                                08FB7795FE84155DC02AAC07 /* mDNS Server Sources */,
                                6575FC1F022EB78C00000109 /* Command-Line Clients */,
+                               213FB20912028902002B3A08 /* Bonjour Events Plugin */,
                                6575FBFE022EAFA800000109 /* MIG files */,
                                DB2CC4420662DCE500335AB3 /* Java Support */,
                                FFFB0DA407B43BED00B88D48 /* PreferencePane */,
                                FFA572600AF1908D0055A0F1 /* DNSServiceDiscovery.h */,
                                FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */,
                                FFA572630AF190C20055A0F1 /* dns_sd.h */,
+                               4BD2B638134FE09F002B96D5 /* P2PPacketFilter.c */,
+                               4BD2B639134FE09F002B96D5 /* P2PPacketFilter.h */,
                        );
                        name = "mDNS Server Sources";
                        sourceTree = "<group>";
                                D284BED90ADD80A20027CCDF /* dnsextd */,
                                D284BEE80ADD80A70027CCDF /* ddnswriteconfig */,
                                D284BF0C0ADD80B00027CCDF /* Bonjour.prefPane */,
-                               FFB765840AEED9C700583A2C /* libdns_sd.a */,
-                               FFA572390AF18F1C0055A0F1 /* libdns_sd_debug.a */,
-                               FFA572450AF18F450055A0F1 /* libdns_sd_profile.a */,
+                               FFB765840AEED9C700583A2C /* libsystem_dnssd.dylib */,
+                               FFA572390AF18F1C0055A0F1 /* libsystem_dnssd_debug.dylib */,
+                               FFA572450AF18F450055A0F1 /* libsystem_dnssd_profile.dylib */,
                                2E0405F00C31955500F13B59 /* mDNSResponderHelper */,
+                               213FB21812028A7A002B3A08 /* BonjourEvents.plugin */,
+                               2141DD1D123FFCDB0086D23E /* libdns_sd.a */,
+                               2141DD24123FFD0F0086D23E /* libdns_sd_debug.a */,
+                               2141DD2A123FFD2C0086D23E /* libdns_sd_profile.a */,
                        );
                        name = Products;
                        sourceTree = "<group>";
                };
+               213FB20912028902002B3A08 /* Bonjour Events Plugin */ = {
+                       isa = PBXGroup;
+                       children = (
+                               213FB22C12028B53002B3A08 /* BonjourEvents.c */,
+                               213FB22D12028B53002B3A08 /* BonjourEvents-Info.plist */,
+                       );
+                       name = "Bonjour Events Plugin";
+                       sourceTree = "<group>";
+               };
                6575FBFE022EAFA800000109 /* MIG files */ = {
                        isa = PBXGroup;
                        children = (
 /* End PBXGroup section */
 
 /* Begin PBXHeadersBuildPhase section */
+               2141DD19123FFCDB0086D23E /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               2141DD20123FFD0F0086D23E /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               2141DD26123FFD2C0086D23E /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                2EC8F8ED0C39CCCA003C9C48 /* Headers */ = {
                        isa = PBXHeadersBuildPhase;
                        buildActionMask = 2147483647;
                                2E8165E80C5980E300485EB2 /* libpfkey.h in Headers */,
                                2E8165EA0C5980F700485EB2 /* ipsec_strerror.h in Headers */,
                                4AE9B04C0F39448B0080B59D /* safe_vproc.h in Headers */,
+                               4BD2B63B134FE09F002B96D5 /* P2PPacketFilter.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
 /* End PBXLegacyTarget section */
 
 /* Begin PBXNativeTarget section */
+               213FB21712028A7A002B3A08 /* BonjourEvents */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 213FB21B12028A7C002B3A08 /* Build configuration list for PBXNativeTarget "BonjourEvents" */;
+                       buildPhases = (
+                               213FB21412028A7A002B3A08 /* Resources */,
+                               213FB21512028A7A002B3A08 /* Sources */,
+                               213FB21612028A7A002B3A08 /* Frameworks */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = BonjourEvents;
+                       productName = BonjourEvents;
+                       productReference = 213FB21812028A7A002B3A08 /* BonjourEvents.plugin */;
+                       productType = "com.apple.product-type.bundle";
+               };
+               2141DD1C123FFCDB0086D23E /* libdns_sd_static */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 2141DD1F123FFCF90086D23E /* Build configuration list for PBXNativeTarget "libdns_sd_static" */;
+                       buildPhases = (
+                               2141DD19123FFCDB0086D23E /* Headers */,
+                               2141DD1A123FFCDB0086D23E /* Sources */,
+                               2141DD1B123FFCDB0086D23E /* Frameworks */,
+                               2130256B12400DE600AC839F /* ShellScript */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = libdns_sd_static;
+                       productName = libdns_sd_static;
+                       productReference = 2141DD1D123FFCDB0086D23E /* libdns_sd.a */;
+                       productType = "com.apple.product-type.library.static";
+               };
+               2141DD23123FFD0F0086D23E /* libdns_sd_debug_static */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 2141DD35123FFD3B0086D23E /* Build configuration list for PBXNativeTarget "libdns_sd_debug_static" */;
+                       buildPhases = (
+                               2141DD20123FFD0F0086D23E /* Headers */,
+                               2141DD21123FFD0F0086D23E /* Sources */,
+                               2141DD22123FFD0F0086D23E /* Frameworks */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = libdns_sd_debug_static;
+                       productName = libdns_sd_debug_static;
+                       productReference = 2141DD24123FFD0F0086D23E /* libdns_sd_debug.a */;
+                       productType = "com.apple.product-type.library.static";
+               };
+               2141DD29123FFD2C0086D23E /* libdns_sd_profile_static */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 2141DD36123FFD3B0086D23E /* Build configuration list for PBXNativeTarget "libdns_sd_profile_static" */;
+                       buildPhases = (
+                               2141DD26123FFD2C0086D23E /* Headers */,
+                               2141DD27123FFD2C0086D23E /* Sources */,
+                               2141DD28123FFD2C0086D23E /* Frameworks */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = libdns_sd_profile_static;
+                       productName = libdns_sd_profile_static;
+                       productReference = 2141DD2A123FFD2C0086D23E /* libdns_sd_profile.a */;
+                       productType = "com.apple.product-type.library.static";
+               };
                2E0405EF0C31955500F13B59 /* mDNSResponderHelper */ = {
                        isa = PBXNativeTarget;
                        buildConfigurationList = 2E0405F30C31956600F13B59 /* Build configuration list for PBXNativeTarget "mDNSResponderHelper" */;
                        productReference = D284BF0C0ADD80B00027CCDF /* Bonjour.prefPane */;
                        productType = "com.apple.product-type.bundle";
                };
-               FFA572300AF18F1C0055A0F1 /* libdns_sd debug */ = {
+               FFA572300AF18F1C0055A0F1 /* libdns_sd_debug_dynamic */ = {
                        isa = PBXNativeTarget;
-                       buildConfigurationList = FFA572370AF18F1C0055A0F1 /* Build configuration list for PBXNativeTarget "libdns_sd debug" */;
+                       buildConfigurationList = FFA572370AF18F1C0055A0F1 /* Build configuration list for PBXNativeTarget "libdns_sd_debug_dynamic" */;
                        buildPhases = (
                                FFA572310AF18F1C0055A0F1 /* Headers */,
                                FFA572320AF18F1C0055A0F1 /* Sources */,
                        );
                        dependencies = (
                        );
-                       name = "libdns_sd debug";
-                       productName = libdns_sd.a;
-                       productReference = FFA572390AF18F1C0055A0F1 /* libdns_sd_debug.a */;
-                       productType = "com.apple.product-type.library.static";
+                       name = libdns_sd_debug_dynamic;
+                       productName = libdns_sd.dylib;
+                       productReference = FFA572390AF18F1C0055A0F1 /* libsystem_dnssd_debug.dylib */;
+                       productType = "com.apple.product-type.library.dynamic";
                };
-               FFA5723C0AF18F450055A0F1 /* libdns_sd profile */ = {
+               FFA5723C0AF18F450055A0F1 /* libdns_sd_profile_dynamic */ = {
                        isa = PBXNativeTarget;
-                       buildConfigurationList = FFA572430AF18F450055A0F1 /* Build configuration list for PBXNativeTarget "libdns_sd profile" */;
+                       buildConfigurationList = FFA572430AF18F450055A0F1 /* Build configuration list for PBXNativeTarget "libdns_sd_profile_dynamic" */;
                        buildPhases = (
                                FFA5723D0AF18F450055A0F1 /* Headers */,
                                FFA5723E0AF18F450055A0F1 /* Sources */,
                        );
                        dependencies = (
                        );
-                       name = "libdns_sd profile";
-                       productName = libdns_sd.a;
-                       productReference = FFA572450AF18F450055A0F1 /* libdns_sd_profile.a */;
-                       productType = "com.apple.product-type.library.static";
+                       name = libdns_sd_profile_dynamic;
+                       productName = libdns_sd.dylib;
+                       productReference = FFA572450AF18F450055A0F1 /* libsystem_dnssd_profile.dylib */;
+                       productType = "com.apple.product-type.library.dynamic";
                };
-               FFB765830AEED9C700583A2C /* libdns_sd */ = {
+               FFB765830AEED9C700583A2C /* libdns_sd_dynamic */ = {
                        isa = PBXNativeTarget;
-                       buildConfigurationList = FFB765890AEED9FB00583A2C /* Build configuration list for PBXNativeTarget "libdns_sd" */;
+                       buildConfigurationList = FFB765890AEED9FB00583A2C /* Build configuration list for PBXNativeTarget "libdns_sd_dynamic" */;
                        buildPhases = (
                                FFB765800AEED9C700583A2C /* Headers */,
                                FFB765810AEED9C700583A2C /* Sources */,
                                FFB765820AEED9C700583A2C /* Frameworks */,
-                               FFA572500AF190070055A0F1 /* CopyFiles */,
                                21DE714D115831CB00DD4BD1 /* ShellScript */,
                        );
                        buildRules = (
                        );
                        dependencies = (
                        );
-                       name = libdns_sd;
-                       productName = libdns_sd.a;
-                       productReference = FFB765840AEED9C700583A2C /* libdns_sd.a */;
-                       productType = "com.apple.product-type.library.static";
+                       name = libdns_sd_dynamic;
+                       productName = libdns_sd.dylib;
+                       productReference = FFB765840AEED9C700583A2C /* libsystem_dnssd.dylib */;
+                       productType = "com.apple.product-type.library.dynamic";
                };
 /* End PBXNativeTarget section */
 
                        isa = PBXProject;
                        buildConfigurationList = D284BE2B0ADD78180027CCDF /* Build configuration list for PBXProject "mDNSResponder" */;
                        compatibilityVersion = "Xcode 3.1";
+                       developmentRegion = English;
                        hasScannedForEncodings = 1;
+                       knownRegions = (
+                               English,
+                               Japanese,
+                               French,
+                               German,
+                       );
                        mainGroup = 08FB7794FE84155DC02AAC07 /* mDNSResponder */;
                        projectDirPath = "";
                        projectRoot = "";
                                D284BEBF0ADD80A20027CCDF /* dnsextd */,
                                D284BEDB0ADD80A70027CCDF /* ddnswriteconfig */,
                                D284BEEA0ADD80B00027CCDF /* PreferencePane */,
-                               FFB765830AEED9C700583A2C /* libdns_sd */,
-                               FFA572300AF18F1C0055A0F1 /* libdns_sd debug */,
-                               FFA5723C0AF18F450055A0F1 /* libdns_sd profile */,
-                               FFA572650AF190F10055A0F1 /* SystemLibraries */,
+                               FFB765830AEED9C700583A2C /* libdns_sd_dynamic */,
+                               FFA572300AF18F1C0055A0F1 /* libdns_sd_debug_dynamic */,
+                               FFA5723C0AF18F450055A0F1 /* libdns_sd_profile_dynamic */,
+                               FFA572650AF190F10055A0F1 /* SystemLibrariesDynamic */,
+                               213FB21712028A7A002B3A08 /* BonjourEvents */,
+                               2141DCF8123FFB5D0086D23E /* SystemLibraries */,
+                               2141DD0B123FFC7F0086D23E /* SystemLibrariesStatic */,
+                               2141DD1C123FFCDB0086D23E /* libdns_sd_static */,
+                               2141DD23123FFD0F0086D23E /* libdns_sd_debug_static */,
+                               2141DD29123FFD2C0086D23E /* libdns_sd_profile_static */,
                        );
                };
 /* End PBXProject section */
 
 /* Begin PBXResourcesBuildPhase section */
+               213FB21412028A7A002B3A08 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                D284BEEE0ADD80B00027CCDF /* Resources */ = {
                        isa = PBXResourcesBuildPhase;
                        buildActionMask = 2147483647;
                        shellPath = /bin/sh;
                        shellScript = "if [ -e \"${SDKROOT}/usr/include/vproc.h\" ]\nthen\nrm -f \"${CONFIGURATION_TEMP_DIR}/vproc.h\"\nelse\ntouch \"${CONFIGURATION_TEMP_DIR}/vproc.h\"\nfi\n\nif [ -e \"${SDKROOT}/usr/lib/libipsec.dylib\" ]\nthen\nrm -f \"${CONFIGURATION_TEMP_DIR}/ipsec_options.h\"\ntouch \"${CONFIGURATION_TEMP_DIR}/ipsec_options.h\"\nrm -f \"${CONFIGURATION_TEMP_DIR}/libipsec.a\"\nelse\necho \"#define MDNS_NO_IPSEC 1\" > ${CONFIGURATION_TEMP_DIR}/ipsec_options.h\ntouch \"${CONFIGURATION_TEMP_DIR}/empty.c\"\nfor i in ${ARCHS}\ndo\nccflags=\"-arch $i $ccflags\"\ndone\ncc ${ccflags} \"${CONFIGURATION_TEMP_DIR}/empty.c\" -c -o \"${CONFIGURATION_TEMP_DIR}/libipsec.a\"\nrm -f \"${CONFIGURATION_TEMP_DIR}/empty.c\"\nfi\n";
                };
+               1F7B473C12B82BFD00868AEF /* ShellScript */ = {
+                       isa = PBXShellScriptBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       inputPaths = (
+                       );
+                       outputPaths = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+                       shellPath = /bin/bash;
+                       shellScript = "# check if we're building for the simulator and patch the \"id\" of the library (as reported by otool -D) to\n# be just /usr/lib/system/libsystem_sim_dnssd.dylib etc. Also remove the usr directory as it is not needed\n# for simulator\nif [ \"${RC_ProjectName%_Sim}\" != \"${RC_ProjectName}\" ] ; then\n\tif [ -d ${DSTROOT}${SDKROOT}/usr/lib/system ] ; then\n\t\tfor lib in ${DSTROOT}${SDKROOT}/usr/lib/system/*.dylib ; do\n\t\t\tinstall_name_tool -id \"${lib#${DSTROOT}${SDKROOT}}\" \"${lib}\"\n\t\tdone\n\tfi\n\trm -rf $DSTROOT/usr\nfi\n";
+               };
+               2130256B12400DE600AC839F /* ShellScript */ = {
+                       isa = PBXShellScriptBuildPhase;
+                       buildActionMask = 8;
+                       files = (
+                       );
+                       inputPaths = (
+                       );
+                       outputPaths = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 1;
+                       shellPath = /bin/sh;
+                       shellScript = "#if we are building for simulator, change the installation path\nif [ \"${RC_ProjectName%_Sim}\" != \"${RC_ProjectName}\" ] ; then\n\tDSTROOT=${DSTROOT}${SDKROOT}\nfi\nmkdir -p \"$DSTROOT/usr/include/DNSServiceDiscovery\"\ncp $SRCROOT/DNSServiceDiscovery.h $DSTROOT/usr/include/DNSServiceDiscovery\nsed 's/\\(^#define _DNS_SD_LIBDISPATCH \\)0$/\\1 1/' \"$SRCROOT/../mDNSShared/dns_sd.h\" > \"$DSTROOT/usr/include/dns_sd.h\"";
+               };
                21DE714D115831CB00DD4BD1 /* ShellScript */ = {
                        isa = PBXShellScriptBuildPhase;
                        buildActionMask = 8;
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/sh;
-                       shellScript = "sed 's/\\(^#define _DNS_SD_LIBDISPATCH \\)0$/\\1 1/' \"$SRCROOT/../mDNSShared/dns_sd.h\" > \"$DSTROOT/usr/include/dns_sd.h\"";
+                       shellScript = "#if we are building for simulator, change the installation path\nif [ \"${RC_ProjectName%_Sim}\" != \"${RC_ProjectName}\" ] ; then\n\tDSTROOT=${DSTROOT}${SDKROOT}\nfi\nmkdir -p \"$DSTROOT/usr/include/DNSServiceDiscovery\"\ncp $SRCROOT/DNSServiceDiscovery.h $DSTROOT/usr/include/DNSServiceDiscovery\nsed 's/\\(^#define _DNS_SD_LIBDISPATCH \\)0$/\\1 1/' \"$SRCROOT/../mDNSShared/dns_sd.h\" > \"$DSTROOT/usr/include/dns_sd.h\"";
                };
                D284BE510ADD80740027CCDF /* ShellScript */ = {
                        isa = PBXShellScriptBuildPhase;
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/bash;
-                       shellScript = "# Install mDNSResponder.bundle containing language localizations\nmkdir -p ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices\ncp -R ${SRCROOT}/mDNSResponder-bundle ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices/mDNSResponder.bundle\n\n# Remove unwanted CVS directories\nfind ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices/mDNSResponder.bundle -depth -name CVS -exec rm -rf {} \\;\n\n# Expand UTF-8 files to UTF-16 (at one time this appeared to be necessary, but it's not, so we don't do it any more)\n#foreach file (`find ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices/mDNSResponder.bundle -name Localizable.strings`)\n#iconv -f utf-8 -t utf-16 ${file} > ${file}.new\n#mv -f ${file}.new ${file}\n#end\n\n# Remove French localization (not wanted for Apple B&I builds)\nrm -rf ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices/mDNSResponder.bundle/Resources/French.lproj\n";
+                       shellScript = "# Install mDNSResponder.bundle containing language localizations\nmkdir -p ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices\ncp -R ${SRCROOT}/mDNSResponder-bundle ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices/mDNSResponder.bundle\n\n# Remove unwanted CVS directories\nfind ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices/mDNSResponder.bundle -depth -name CVS -exec rm -rf {} \\;\n\n# Expand UTF-8 files to UTF-16 (at one time this appeared to be necessary, but it's not, so we don't do it any more)\n#foreach file (`find ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices/mDNSResponder.bundle -name Localizable.strings`)\n#iconv -f utf-8 -t utf-16 ${file} > ${file}.new\n#mv -f ${file}.new ${file}\n#end\n\n# Remove French localization (not wanted for Apple B&I builds)\nrm -rf ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices/mDNSResponder.bundle/Resources/French.lproj\n\n# Copy Sandbox profile\nif [ ! -z \"${IPHONEOS_DEPLOYMENT_TARGET}\" ] ; then\n  SANDBOXDST=\"${DSTROOT}/usr/local/share/sandbox/profiles/embedded/builtin\"\nelse\n  SANDBOXDST=\"${DSTROOT}/usr/share/sandbox\"\nfi\n(umask 022; mkdir -p -m 0755 \"$SANDBOXDST\")\ncp \"${SRCROOT}/mDNSResponder.sb\" \"${SANDBOXDST}/mDNSResponder.sb\"\n";
                };
                D284BE760ADD80800027CCDF /* ShellScript */ = {
                        isa = PBXShellScriptBuildPhase;
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/sh;
-                       shellScript = "# Install plists to tell launchd how to start mDNSResponder and mDNSResponderHelper\n\nmkdir -p ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons\n\nif [ \"${MACOSX_DEPLOYMENT_TARGET}\" == \"10.4\" ] ; then\ncp ${SRCROOT}/LaunchDaemonInfo-Tiger.plist        ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponder.plist\ncp ${SRCROOT}/LaunchDaemonInfo-Tiger.helper.plist ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponderHelper.plist\nelse\ncp ${SRCROOT}/LaunchDaemonInfo.plist              ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponder.plist\ncp ${SRCROOT}/LaunchDaemonInfo.helper.plist       ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponderHelper.plist\nfi\n\nif [ ! -z \"${IPHONEOS_DEPLOYMENT_TARGET}\" ] ; then\nplutil -convert binary1 ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponder.plist\nplutil -convert binary1 ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponderHelper.plist\nfi\n\n\n# Install Sandbox profile\n\nif [ ! -z \"${IPHONEOS_DEPLOYMENT_TARGET}\" ] ; then\n  SANDBOXDST=\"${DSTROOT}/usr/local/share/sandbox/profiles/embedded/builtin\"\nelse\n  SANDBOXDST=\"${DSTROOT}/usr/share/sandbox\"\nfi\n\n(umask 022; mkdir -p -m 0755 \"$SANDBOXDST\")\n\nif [[ \"${MACOSX_DEPLOYMENT_TARGET}\" == 10.[0-6] ]] ; then\n  sed '/iokit-open/d' \"${SRCROOT}/mDNSResponder.sb\" > \"${SANDBOXDST}/mDNSResponder.sb\"\nelse\n  cp \"${SRCROOT}/mDNSResponder.sb\" \"${SANDBOXDST}/mDNSResponder.sb\"\nfi\n";
+                       shellScript = "# Install plists to tell launchd how to start mDNSResponder and mDNSResponderHelper\nmkdir -p ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons\n\nif [ \"${MACOSX_DEPLOYMENT_TARGET}\" == \"10.4\" ] ; then\ncp ${SRCROOT}/LaunchDaemonInfo-Tiger.plist        ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponder.plist\ncp ${SRCROOT}/LaunchDaemonInfo-Tiger.helper.plist ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponderHelper.plist\nelse\ncp ${SRCROOT}/LaunchDaemonInfo.plist              ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponder.plist\ncp ${SRCROOT}/LaunchDaemonInfo.helper.plist       ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponderHelper.plist\nfi\n\nif [ ! -z \"${IPHONEOS_DEPLOYMENT_TARGET}\" ] ; then\nplutil -convert binary1 ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponder.plist\nplutil -convert binary1 ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponderHelper.plist\nfi\n";
                };
                FF37FAAD0BC581780044A5CF /* ShellScript */ = {
                        isa = PBXShellScriptBuildPhase;
 /* End PBXShellScriptBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
+               213FB21512028A7A002B3A08 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               213FB23C12028C4A002B3A08 /* BonjourEvents.c in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               2141DD1A123FFCDB0086D23E /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               215FFAEE124000F900470DE1 /* dnssd_ipc.c in Sources */,
+                               215FFAEF124000F900470DE1 /* dnssd_clientlib.c in Sources */,
+                               215FFAF0124000F900470DE1 /* dnssd_clientstub.c in Sources */,
+                               215FFAF1124000F900470DE1 /* DNSServiceDiscovery.c in Sources */,
+                               215FFAF2124000F900470DE1 /* DNSServiceDiscoveryRequest.defs in Sources */,
+                               215FFAF3124000F900470DE1 /* DNSServiceDiscoveryReply.defs in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               2141DD21123FFD0F0086D23E /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               215FFAF41240011800470DE1 /* dnssd_ipc.c in Sources */,
+                               215FFAF51240011800470DE1 /* dnssd_clientlib.c in Sources */,
+                               215FFAF61240011800470DE1 /* dnssd_clientstub.c in Sources */,
+                               215FFAF71240011800470DE1 /* DNSServiceDiscovery.c in Sources */,
+                               215FFAF81240011800470DE1 /* DNSServiceDiscoveryRequest.defs in Sources */,
+                               215FFAF91240011800470DE1 /* DNSServiceDiscoveryReply.defs in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               2141DD27123FFD2C0086D23E /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               215FFAFA1240013400470DE1 /* dnssd_ipc.c in Sources */,
+                               215FFAFB1240013400470DE1 /* dnssd_clientlib.c in Sources */,
+                               215FFAFC1240013400470DE1 /* dnssd_clientstub.c in Sources */,
+                               215FFAFD1240013400470DE1 /* DNSServiceDiscovery.c in Sources */,
+                               215FFAFE1240013400470DE1 /* DNSServiceDiscoveryRequest.defs in Sources */,
+                               215FFAFF1240013400470DE1 /* DNSServiceDiscoveryReply.defs in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                2E0405ED0C31955500F13B59 /* Sources */ = {
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                                2E96A51D0C39BDAC0087C4D2 /* helper-main.c in Sources */,
                                2E8165E90C5980EE00485EB2 /* pfkey.c in Sources */,
                                4AE9B04B0F39448B0080B59D /* safe_vproc.c in Sources */,
+                               4BD2B63A134FE09F002B96D5 /* P2PPacketFilter.c in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        target = 03067D640C83A3700022BE1F /* Build Some */;
                        targetProxy = 03067D850C849CC30022BE1F /* PBXContainerItemProxy */;
                };
+               2130257112400E9300AC839F /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = FFA572650AF190F10055A0F1 /* SystemLibrariesDynamic */;
+                       targetProxy = 2130257012400E9300AC839F /* PBXContainerItemProxy */;
+               };
+               2141DCFD123FFB7D0086D23E /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = 2141DCF8123FFB5D0086D23E /* SystemLibraries */;
+                       targetProxy = 2141DCFC123FFB7D0086D23E /* PBXContainerItemProxy */;
+               };
+               2141DD0E123FFC960086D23E /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = 2141DD0B123FFC7F0086D23E /* SystemLibrariesStatic */;
+                       targetProxy = 2141DD0D123FFC960086D23E /* PBXContainerItemProxy */;
+               };
+               215FFB19124002C100470DE1 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = 2141DD1C123FFCDB0086D23E /* libdns_sd_static */;
+                       targetProxy = 215FFB18124002C100470DE1 /* PBXContainerItemProxy */;
+               };
+               215FFB1B124002C700470DE1 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = 2141DD23123FFD0F0086D23E /* libdns_sd_debug_static */;
+                       targetProxy = 215FFB1A124002C700470DE1 /* PBXContainerItemProxy */;
+               };
+               215FFB1D124002CC00470DE1 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = 2141DD29123FFD2C0086D23E /* libdns_sd_profile_static */;
+                       targetProxy = 215FFB1C124002CC00470DE1 /* PBXContainerItemProxy */;
+               };
+               216EF8EC120295AA004917D2 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = 213FB21712028A7A002B3A08 /* BonjourEvents */;
+                       targetProxy = 216EF8EB120295AA004917D2 /* PBXContainerItemProxy */;
+               };
                4AE4716A0EAFF83800A6C5AD /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        target = 4AE471670EAFF81900A6C5AD /* dns_sd.jar */;
                };
                FFA572690AF190FF0055A0F1 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
-                       target = FFB765830AEED9C700583A2C /* libdns_sd */;
+                       target = FFB765830AEED9C700583A2C /* libdns_sd_dynamic */;
                        targetProxy = FFA572680AF190FF0055A0F1 /* PBXContainerItemProxy */;
                };
                FFA5726B0AF191010055A0F1 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
-                       target = FFA572300AF18F1C0055A0F1 /* libdns_sd debug */;
+                       target = FFA572300AF18F1C0055A0F1 /* libdns_sd_debug_dynamic */;
                        targetProxy = FFA5726A0AF191010055A0F1 /* PBXContainerItemProxy */;
                };
                FFA5726D0AF191020055A0F1 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
-                       target = FFA5723C0AF18F450055A0F1 /* libdns_sd profile */;
+                       target = FFA5723C0AF18F450055A0F1 /* libdns_sd_profile_dynamic */;
                        targetProxy = FFA5726C0AF191020055A0F1 /* PBXContainerItemProxy */;
                };
-               FFA572710AF191230055A0F1 /* PBXTargetDependency */ = {
-                       isa = PBXTargetDependency;
-                       target = FFA572650AF190F10055A0F1 /* SystemLibraries */;
-                       targetProxy = FFA572700AF191230055A0F1 /* PBXContainerItemProxy */;
-               };
                FFAE66F9105F0CF100162116 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        target = D284BEDB0ADD80A70027CCDF /* ddnswriteconfig */;
                        };
                        name = Development;
                };
+               213FB21A12028A7B002B3A08 /* Development */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               BUNDLE_LOADER = /usr/libexec/UserEventAgent;
+                               COPY_PHASE_STRIP = NO;
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_MODEL_TUNING = G5;
+                               INFOPLIST_FILE = "BonjourEvents-Info.plist";
+                               INSTALL_PATH = /System/Library/UserEventPlugins/;
+                               PREBINDING = NO;
+                               PRODUCT_NAME = BonjourEvents;
+                               WRAPPER_EXTENSION = plugin;
+                       };
+                       name = Development;
+               };
+               2141DCF9123FFB5D0086D23E /* Development */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               COPY_PHASE_STRIP = NO;
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               PRODUCT_NAME = SystemLibraries;
+                       };
+                       name = Development;
+               };
+               2141DD0C123FFC7F0086D23E /* Development */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               COPY_PHASE_STRIP = NO;
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               PRODUCT_NAME = SystemLibrariesStatic;
+                       };
+                       name = Development;
+               };
+               2141DD1E123FFCDB0086D23E /* Development */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
+                               CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
+                               COPY_PHASE_STRIP = NO;
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "$(inherited)",
+                                       "__DARWIN_NON_CANCELABLE=1",
+                               );
+                               HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/";
+                               INSTALLHDRS_COPY_PHASE = YES;
+                               INSTALLHDRS_SCRIPT_PHASE = YES;
+                               INSTALL_PATH = /usr/local/lib/system;
+                               PREBINDING = NO;
+                               PRODUCT_NAME = dns_sd;
+                       };
+                       name = Development;
+               };
+               2141DD25123FFD100086D23E /* Development */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
+                               CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
+                               COPY_PHASE_STRIP = NO;
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "$(inherited)",
+                                       "__DARWIN_NON_CANCELABLE=1",
+                               );
+                               HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/";
+                               INSTALL_PATH = /usr/local/lib/system;
+                               PREBINDING = NO;
+                               PRODUCT_NAME = dns_sd_debug;
+                       };
+                       name = Development;
+               };
+               2141DD2B123FFD2C0086D23E /* Development */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
+                               CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
+                               COPY_PHASE_STRIP = NO;
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "$(inherited)",
+                                       "__DARWIN_NON_CANCELABLE=1",
+                               );
+                               HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/";
+                               INSTALL_PATH = /usr/local/lib/system;
+                               PREBINDING = NO;
+                               PRODUCT_NAME = dns_sd_profile;
+                       };
+                       name = Development;
+               };
                2E0405F20C31955500F13B59 /* Development */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                CONFIGURATION_TEMP_DIR = "$(PROJECT_TEMP_DIR)";
                                COPY_PHASE_STRIP = NO;
                                GCC_DYNAMIC_NO_PIC = NO;
-                               GCC_ENABLE_FIX_AND_CONTINUE = NO;
                                GCC_WARN_CHECK_SWITCH_STATEMENTS = NO;
-                               HEADER_SEARCH_PATHS = "${CONFIGURATION_TEMP_DIR}";
+                               HEADER_SEARCH_PATHS = (
+                                       "${CONFIGURATION_TEMP_DIR}",
+                                       "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders",
+                               );
                                INSTALL_PATH = /usr/sbin;
                                LD_MAP_FILE_PATH = "$(TARGET_TEMP_DIR)/$(PRODUCT_NAME)-LinkMap-$(CURRENT_VARIANT)-$(CURRENT_ARCH).txt";
                                LIBRARY_SEARCH_PATHS = "\"${CONFIGURATION_TEMP_DIR}\"";
                                        "$(inherited)",
                                        "-lipsec",
                                );
+                               "OTHER_LDFLAGS[sdk=iphoneos*] [arch=*]" = "-lipsec -Wl,-pie";
+                               "OTHER_LDFLAGS[sdk=macosx10.7][arch=*]" = (
+                                       "-lipsec",
+                                       "-Wl,-pie",
+                               );
                                PREBINDING = NO;
                                PRODUCT_NAME = mDNSResponderHelper;
                        };
                                CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
                                CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
                                GCC_DYNAMIC_NO_PIC = NO;
-                               GCC_ENABLE_FIX_AND_CONTINUE = NO;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
                                OTHER_CFLAGS = "";
                                OTHER_LDFLAGS = "";
                                CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)";
                                DEAD_CODE_STRIPPING = YES;
                                GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "__APPLE_USE_RFC_3542=1",
                                        "_DNS_SD_LIBDISPATCH=1",
                                        "APPLE_OSX_mDNSResponder=1",
                                        "__MigTypeCheck=1",
                                CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
                                FRAMEWORK_SEARCH_PATHS = (
                                        "$(inherited)",
-                                       "\"$(SDKROOT)/$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks\"",
+                                       "$(SDKROOT)/$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
                                );
                                GCC_DYNAMIC_NO_PIC = NO;
-                               GCC_ENABLE_FIX_AND_CONTINUE = NO;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
                                HEADER_SEARCH_PATHS = (
                                        ../mDNSShared,
                                );
                                OTHER_LDFLAGS = "";
                                "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+                                       "-Wl,-pie",
                                        "-weak_framework",
                                        DeviceToDeviceManager,
                                );
                                "OTHER_LDFLAGS[sdk=macosx10.6][arch=*]" = "-lAWACS";
                                "OTHER_LDFLAGS[sdk=macosx10.7][arch=*]" = (
+                                       "-Wl,-pie",
                                        "-lAWACS",
                                        "-weak_framework",
                                        WebFilterDNS,
                                CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
                                FRAMEWORK_SEARCH_PATHS = (
                                        "$(inherited)",
-                                       "\"$(SDKROOT)/$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks\"",
+                                       "$(SDKROOT)/$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
                                );
                                GCC_DYNAMIC_NO_PIC = NO;
-                               GCC_ENABLE_FIX_AND_CONTINUE = YES;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
                                GCC_OPTIMIZATION_LEVEL = 0;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                );
                                OTHER_LDFLAGS = "";
                                "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
+                                       "-Wl,-pie",
                                        "-weak_framework",
                                        DeviceToDeviceManager,
                                );
                                "OTHER_LDFLAGS[sdk=macosx10.6][arch=*]" = "-lAWACS";
                                "OTHER_LDFLAGS[sdk=macosx10.7][arch=*]" = (
+                                       "-Wl,-pie",
                                        "-lAWACS",
                                        "-weak_framework",
                                        WebFilterDNS,
                                        __text,
                                        mDNSResponder.order,
                                );
+                               SKIP_INSTALL = YES;
                        };
                        name = Development;
                };
                                CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
                                CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
                                GCC_DYNAMIC_NO_PIC = NO;
-                               GCC_ENABLE_FIX_AND_CONTINUE = NO;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
                                INSTALL_PATH = /usr/bin;
                                OTHER_CFLAGS = "-no-cpp-precomp";
                                CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
                                CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
                                GCC_DYNAMIC_NO_PIC = NO;
-                               GCC_ENABLE_FIX_AND_CONTINUE = NO;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
                                HEADER_SEARCH_PATHS = ../mDNSShared;
                                INSTALL_PATH = /usr/bin;
                                DYLIB_CURRENT_VERSION = 1;
                                EXECUTABLE_EXTENSION = jnilib;
                                GCC_DYNAMIC_NO_PIC = NO;
-                               GCC_ENABLE_FIX_AND_CONTINUE = NO;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = NO;
                                HEADER_SEARCH_PATHS = (
                                CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
                                FRAMEWORK_SEARCH_PATHS = "";
                                GCC_DYNAMIC_NO_PIC = NO;
-                               GCC_ENABLE_FIX_AND_CONTINUE = NO;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
                                HEADER_SEARCH_PATHS = (
                                        "${APPLE_INTERNAL_DEVELOPER_DIR}/Headers",
                                LEXFLAGS = "";
                                LEX_CASE_INSENSITIVE_SCANNER = YES;
                                LIBRARY_SEARCH_PATHS = "\"${CONFIGURATION_TEMP_DIR}\"";
-                               MACOSX_DEPLOYMENT_TARGET = 10.4;
+                               MACOSX_DEPLOYMENT_TARGET = 10.5;
                                OTHER_CFLAGS = (
                                        "-no-cpp-precomp",
                                        "-UAPPLE_OSX_mDNSResponder",
                                );
                                OTHER_LDFLAGS = "";
+                               "OTHER_LDFLAGS[sdk=macosx10.7][arch=*]" = "-Wl,-pie";
                                OTHER_REZFLAGS = "";
                                PRODUCT_NAME = dnsextd;
                                REZ_EXECUTABLE = YES;
                                CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
                                CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
                                GCC_DYNAMIC_NO_PIC = NO;
-                               GCC_ENABLE_FIX_AND_CONTINUE = NO;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
                                INSTALL_PATH = "/Library/Application Support/Bonjour";
                                MACOSX_DEPLOYMENT_TARGET = 10.5;
                                CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
                                EXPORTED_SYMBOLS_FILE = "";
                                GCC_DYNAMIC_NO_PIC = NO;
-                               GCC_ENABLE_FIX_AND_CONTINUE = NO;
                                GCC_ENABLE_OBJC_GC = supported;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = NO;
                                CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
                                CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
                                COPY_PHASE_STRIP = NO;
+                               EXECUTABLE_EXTENSION = dylib;
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                        "__DARWIN_NON_CANCELABLE=1",
                                );
                                HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/";
-                               INSTALL_PATH = /usr/local/lib/system;
-                               PRODUCT_NAME = dns_sd_debug;
+                               INSTALL_PATH = /usr/lib/system;
+                               "INSTALL_PATH[sdk=iphonesimulator*]" = "$(SDKROOT)/usr/lib/system";
+                               OTHER_LDFLAGS = (
+                                       "-umbrella",
+                                       System,
+                               );
+                               "OTHER_LDFLAGS[sdk=iphonesimulator*][arch=*]" = "";
+                               PRODUCT_NAME = libsystem_dnssd_debug;
+                               "PRODUCT_NAME[sdk=iphonesimulator*]" = libsystem_sim_dnssd_debug;
                        };
                        name = Development;
                };
                                CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
                                CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
                                COPY_PHASE_STRIP = NO;
+                               EXECUTABLE_EXTENSION = dylib;
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                );
                                GENERATE_PROFILING_CODE = YES;
                                HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/";
-                               INSTALL_PATH = /usr/local/lib/system;
-                               PRODUCT_NAME = dns_sd_profile;
+                               INSTALL_PATH = /usr/lib/system;
+                               "INSTALL_PATH[sdk=iphonesimulator*]" = "$(SDKROOT)/usr/lib/system";
+                               OTHER_LDFLAGS = (
+                                       "-umbrella",
+                                       System,
+                               );
+                               "OTHER_LDFLAGS[sdk=iphonesimulator*][arch=*]" = "";
+                               PRODUCT_NAME = libsystem_dnssd_profile;
+                               "PRODUCT_NAME[sdk=iphonesimulator*]" = libsystem_sim_dnssd_profile;
                        };
                        name = Development;
                };
                                COPY_PHASE_STRIP = NO;
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
-                               PRODUCT_NAME = SystemLibraries;
+                               PRODUCT_NAME = SystemLibrariesDynamic;
                        };
                        name = Development;
                };
                                CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
                                CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
                                COPY_PHASE_STRIP = NO;
+                               EXECUTABLE_EXTENSION = dylib;
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/";
                                INSTALLHDRS_COPY_PHASE = YES;
                                INSTALLHDRS_SCRIPT_PHASE = YES;
-                               INSTALL_PATH = /usr/local/lib/system;
-                               PRODUCT_NAME = dns_sd;
+                               INSTALL_PATH = /usr/lib/system;
+                               "INSTALL_PATH[sdk=iphonesimulator*]" = "$(SDKROOT)/usr/lib/system";
+                               OTHER_LDFLAGS = (
+                                       "-umbrella",
+                                       System,
+                               );
+                               "OTHER_LDFLAGS[sdk=iphonesimulator*][arch=*]" = "";
+                               PRODUCT_NAME = libsystem_dnssd;
+                               "PRODUCT_NAME[sdk=iphonesimulator*]" = libsystem_sim_dnssd;
                        };
                        name = Development;
                };
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Development;
                };
+               213FB21B12028A7C002B3A08 /* Build configuration list for PBXNativeTarget "BonjourEvents" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               213FB21A12028A7B002B3A08 /* Development */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Development;
+               };
+               2141DD08123FFB830086D23E /* Build configuration list for PBXAggregateTarget "SystemLibraries" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               2141DCF9123FFB5D0086D23E /* Development */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Development;
+               };
+               2141DD18123FFC990086D23E /* Build configuration list for PBXAggregateTarget "SystemLibrariesStatic" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               2141DD0C123FFC7F0086D23E /* Development */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Development;
+               };
+               2141DD1F123FFCF90086D23E /* Build configuration list for PBXNativeTarget "libdns_sd_static" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               2141DD1E123FFCDB0086D23E /* Development */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Development;
+               };
+               2141DD35123FFD3B0086D23E /* Build configuration list for PBXNativeTarget "libdns_sd_debug_static" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               2141DD25123FFD100086D23E /* Development */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Development;
+               };
+               2141DD36123FFD3B0086D23E /* Build configuration list for PBXNativeTarget "libdns_sd_profile_static" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               2141DD2B123FFD2C0086D23E /* Development */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Development;
+               };
                2E0405F30C31956600F13B59 /* Build configuration list for PBXNativeTarget "mDNSResponderHelper" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Development;
                };
-               FFA572370AF18F1C0055A0F1 /* Build configuration list for PBXNativeTarget "libdns_sd debug" */ = {
+               FFA572370AF18F1C0055A0F1 /* Build configuration list for PBXNativeTarget "libdns_sd_debug_dynamic" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
                                FFA572380AF18F1C0055A0F1 /* Development */,
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Development;
                };
-               FFA572430AF18F450055A0F1 /* Build configuration list for PBXNativeTarget "libdns_sd profile" */ = {
+               FFA572430AF18F450055A0F1 /* Build configuration list for PBXNativeTarget "libdns_sd_profile_dynamic" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
                                FFA572440AF18F450055A0F1 /* Development */,
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Development;
                };
-               FFA5726E0AF191200055A0F1 /* Build configuration list for PBXAggregateTarget "SystemLibraries" */ = {
+               FFA5726E0AF191200055A0F1 /* Build configuration list for PBXAggregateTarget "SystemLibrariesDynamic" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
                                FFA5726F0AF191200055A0F1 /* Development */,
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Development;
                };
-               FFB765890AEED9FB00583A2C /* Build configuration list for PBXNativeTarget "libdns_sd" */ = {
+               FFB765890AEED9FB00583A2C /* Build configuration list for PBXNativeTarget "libdns_sd_dynamic" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
                                FFB7658A0AEED9FB00583A2C /* Development */,
index 6bc9a24f83ef2fee6c22ddc51622535b69f25745..2ca9fc6f2dfffe1fabdd63035e5783905539dfc1 100644 (file)
@@ -221,7 +221,12 @@ mDNSlocal mStatus StartQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const m
        q->ForceMCast       = mDNStrue;         // Query via multicast, even for apparently uDNS names like 1.1.1.17.in-addr.arpa.
        q->ReturnIntermed   = mDNStrue;
        q->SuppressUnusable = mDNSfalse;
-       q->WakeOnResolve    = mDNSfalse;
+       q->SearchListIndex  = 0;
+       q->AppendSearchDomains = 0;
+       q->RetryWithSearchDomains = mDNSfalse;
+       q->TimeoutQuestion  = 0;
+       q->WakeOnResolve    = 0;
+       q->qnameOrig        = mDNSNULL;
        q->QuestionCallback = callback;
        q->QuestionContext  = NULL;
 
index 9390f72ee1f6fb442e104f743f60ba82cbbb3eb5..6ca90136778e9bba9dee9cbe1c8e670d61d639b6 100755 (executable)
@@ -141,7 +141,7 @@ ifeq ($(os),x)
 # we get build failures: ‘daemon’ is deprecated (declared at /usr/include/stdlib.h:283)
 CFLAGS_OS = -DHAVE_IPV6 -no-cpp-precomp -Werror -Wdeclaration-after-statement \
        -D__MAC_OS_X_VERSION_MIN_REQUIRED=__MAC_OS_X_VERSION_10_4 #-Wunreachable-code
-CC = @gcc-4.0
+CC = gcc
 LD = $(CC) -dynamiclib
 LINKOPTS = -lSystem
 LDSUFFIX = dylib
index 771d52d63d5d3dc0f266973fffbd680153d3374a..a10a4d7754ac71ea0177078d9545227e9f422f53 100644 (file)
@@ -478,7 +478,7 @@ mDNSlocal void DisplayPacketHeader(mDNS *const m, const DNSMessage *const msg, c
 
        struct timeval tv;
        struct tm tm;
-       const mDNSu32 index = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID);
+       const mDNSu32 index = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNSfalse);
        char if_name[IFNAMSIZ];         // Older Linux distributions don't define IF_NAMESIZE
        if_indextoname(index, if_name);
        gettimeofday(&tv, NULL);
@@ -795,7 +795,7 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNS
 
        // For now we're only interested in monitoring IPv4 traffic.
        // All IPv6 packets should just be duplicates of the v4 packets.
-       if (!goodinterface) goodinterface = (FilterInterface == (int)mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID));
+       if (!goodinterface) goodinterface = (FilterInterface == (int)mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNSfalse));
        if (goodinterface && AddressMatchesFilterList(srcaddr))
                {
                mDNS_Lock(m);
@@ -841,9 +841,8 @@ mDNSlocal mStatus mDNSNetMonitor(void)
        gStopEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
        if (gStopEvent == INVALID_HANDLE_VALUE) return mStatus_UnknownErr;
        if (!SetConsoleCtrlHandler(ConsoleControlHandler, TRUE)) return mStatus_UnknownErr;
-       SetSocketEventsEnabled(&mDNSStorage, TRUE);
-       while (WaitForSingleObjectEx(gStopEvent, INFINITE, TRUE) == WAIT_IO_COMPLETION);
-       SetSocketEventsEnabled(&mDNSStorage, FALSE);
+       while (WaitForSingleObjectEx(gStopEvent, INFINITE, TRUE) == WAIT_IO_COMPLETION)
+               DispatchSocketEvents(&mDNSStorage);
        if (!SetConsoleCtrlHandler(ConsoleControlHandler, FALSE)) return mStatus_UnknownErr;
        CloseHandle(gStopEvent);
 #else
index e91bcf3061e398a48776bba6c979d4f56867c0f0..ebbbaa70a4179a584bbb9627bee27c097928f854 100644 (file)
@@ -69,8 +69,8 @@ mDNSlocal mStatus mDNS_RegisterProxyHost(mDNS *m, ProxyHost *p)
        {
        char buffer[32];
        
-       mDNS_SetupResourceRecord(&p->RR_A,   mDNSNULL, mDNSInterface_Any, kDNSType_A,   60, kDNSRecordTypeUnique,      HostNameCallback, p);
-       mDNS_SetupResourceRecord(&p->RR_PTR, mDNSNULL, mDNSInterface_Any, kDNSType_PTR, 60, kDNSRecordTypeKnownUnique, HostNameCallback, p);
+       mDNS_SetupResourceRecord(&p->RR_A,   mDNSNULL, mDNSInterface_Any, kDNSType_A,   60, kDNSRecordTypeUnique,      AuthRecordAny, HostNameCallback, p);
+       mDNS_SetupResourceRecord(&p->RR_PTR, mDNSNULL, mDNSInterface_Any, kDNSType_PTR, 60, kDNSRecordTypeKnownUnique, AuthRecordAny, HostNameCallback, p);
 
        p->RR_A.namestorage.c[0] = 0;
        AppendDomainLabel(&p->RR_A.namestorage, &p->hostlabel);
@@ -158,7 +158,7 @@ mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset,
                txtbuffer, bptr-txtbuffer,      // TXT data, length
                mDNSNULL, 0,                            // Subtypes
                mDNSInterface_Any,                      // Interface ID
-               ServiceCallback, mDNSNULL);     // Callback and context
+               ServiceCallback, mDNSNULL, 0);  // Callback, context, flags
 
        ConvertDomainNameToCString(recordset->RR_SRV.resrec.name, buffer);
        printf("Made Service Records for %s\n", buffer);
@@ -198,7 +198,7 @@ mDNSlocal void NoSuchServiceCallback(mDNS *const m, AuthRecord *const rr, mStatu
                ConvertDomainNameToCString(rr->resrec.name, buffer1);
                DeconstructServiceName(rr->resrec.name, &n, &t, &d);
                IncrementLabelSuffix(&n, mDNStrue);
-               mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, mDNSNULL);
+               mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, mDNSNULL, mDNSfalse);
                ConvertDomainNameToCString(rr->resrec.name, buffer2);
                printf("Name Conflict! %s renamed as %s\n", buffer1, buffer2);
                }
@@ -213,7 +213,7 @@ mDNSlocal void RegisterNoSuchService(mDNS *m, AuthRecord *const rr, domainname *
        MakeDomainLabelFromLiteralString(&n, name);
        MakeDomainNameFromDNSNameString(&t, type);
        MakeDomainNameFromDNSNameString(&d, domain);
-       mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, proxyhostname);
+       mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, proxyhostname, mDNSfalse);
        ConvertDomainNameToCString(rr->resrec.name, buffer);
        printf("Made Non-existence Record for %s\n", buffer);
        }
index 9e04dd399187b8d915624bceac59cc82442a4e28..21109b835cd495636d9c05cfe7145d6b721a10a0 100755 (executable)
@@ -442,7 +442,7 @@ static mStatus RegisterOneService(const char *  richTextName,
                 text, textLen,                                         // TXT data, length
                 NULL, 0,                                                       // Subtypes
                 mDNSInterface_Any,                                     // Interface ID
-                RegistrationCallback, thisServ);       // Callback and context
+                RegistrationCallback, thisServ, 0);    // Callback, context, flags
     }
     if (status == mStatus_NoError) {
         thisServ->serviceID = gServiceID;
index 45aa621a913b4298e328f1375435510dad97c708..3db5266ff217d9d060f4a451d2e7e7cc5ddbcffc 100755 (executable)
@@ -411,6 +411,26 @@ mDNSexport void mDNSPlatformTLSTearDownCerts(void)
        {
        }
 
+mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason)
+       {
+       (void) m;
+       (void) allowSleep;
+       (void) reason;
+       }
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - /etc/hosts support
+#endif
+
+mDNSexport void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result)
+    {
+    (void)m;  // unused
+       (void)rr;
+       (void)result;
+       }
+
+
 #if COMPILER_LIKES_PRAGMA_MARK
 #pragma mark ***** DDNS Config Platform Functions
 #endif
@@ -482,7 +502,7 @@ mDNSexport int ParseDNSServers(mDNS *m, const char *filePath)
                        mDNSAddr DNSAddr;
                        DNSAddr.type = mDNSAddrType_IPv4;
                        DNSAddr.ip.v4.NotAnInteger = ina.s_addr;
-                       mDNS_AddDNSServer(m, NULL, mDNSInterface_Any, &DNSAddr, UnicastDNSPort, mDNSfalse);
+                       mDNS_AddDNSServer(m, NULL, mDNSInterface_Any, &DNSAddr, UnicastDNSPort, mDNSfalse, 0);
                        numOfServers++;
                        }
                }  
@@ -522,9 +542,10 @@ mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(mDNS *const
        return (mDNSInterfaceID) intf;
        }
        
-mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id)
+mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id, mDNSBool suppressNetworkChange)
        {
        PosixNetworkInterface *intf;
+       (void) suppressNetworkChange; // Unused
 
        assert(m != NULL);
 
@@ -1368,6 +1389,14 @@ mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID Inte
        (void) iteration;
        }
 
+mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf)
+       {
+       (void) rr;
+       (void) intf;
+
+       return 1;
+       }
+
 mDNSlocal void mDNSPosixAddToFDSet(int *nfds, fd_set *readfds, int s)
        {
        if (*nfds < s + 1) *nfds = s + 1;
index 741388596f8281b5241a8ef2d361b646b34593d2..ed965014943d0f54f27b60a9351516b4bc77148d 100755 (executable)
@@ -73,7 +73,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ControlPanelLocRes", "mDNSW
 EndProject\r
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ControlPanelRes", "mDNSWindows\ControlPanel\ControlPanelRes.vcproj", "{5254AA9C-3D2E-4539-86D9-5EB0F4151215}"\r
 EndProject\r
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ControlPanel", "mDNSWindows\ControlPanel\ControlPanel.vcproj", "{F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}"\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ControlPanel", "mDNSWindows\ControlPanel\ControlPanel.vcproj", "{0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}"\r
        ProjectSection(ProjectDependencies) = postProject\r
                {3A2B6325-3053-4236-84BD-AA9BE2E323E5} = {3A2B6325-3053-4236-84BD-AA9BE2E323E5}\r
        EndProjectSection\r
@@ -367,20 +367,20 @@ Global
                {5254AA9C-3D2E-4539-86D9-5EB0F4151215}.Release|Win32.Build.0 = Release|Win32\r
                {5254AA9C-3D2E-4539-86D9-5EB0F4151215}.Release|x64.ActiveCfg = Release|x64\r
                {5254AA9C-3D2E-4539-86D9-5EB0F4151215}.Release|x64.Build.0 = Release|x64\r
-               {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Debug|Any CPU.ActiveCfg = Debug|x64\r
-               {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Debug|Mixed Platforms.ActiveCfg = Debug|x64\r
-               {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Debug|Mixed Platforms.Build.0 = Debug|x64\r
-               {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Debug|Win32.ActiveCfg = Debug|Win32\r
-               {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Debug|Win32.Build.0 = Debug|Win32\r
-               {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Debug|x64.ActiveCfg = Debug|x64\r
-               {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Debug|x64.Build.0 = Debug|x64\r
-               {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Release|Any CPU.ActiveCfg = Release|x64\r
-               {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Release|Mixed Platforms.ActiveCfg = Release|x64\r
-               {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Release|Mixed Platforms.Build.0 = Release|x64\r
-               {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Release|Win32.ActiveCfg = Release|Win32\r
-               {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Release|Win32.Build.0 = Release|Win32\r
-               {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Release|x64.ActiveCfg = Release|x64\r
-               {F5D703B6-5612-4381-8BE2-2B7AEBAE58FC}.Release|x64.Build.0 = Release|x64\r
+               {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|Any CPU.ActiveCfg = Debug|x64\r
+               {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|Mixed Platforms.ActiveCfg = Debug|x64\r
+               {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|Mixed Platforms.Build.0 = Debug|x64\r
+               {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|Win32.ActiveCfg = Debug|Win32\r
+               {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|Win32.Build.0 = Debug|Win32\r
+               {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|x64.ActiveCfg = Debug|x64\r
+               {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|x64.Build.0 = Debug|x64\r
+               {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|Any CPU.ActiveCfg = Release|x64\r
+               {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|Mixed Platforms.ActiveCfg = Release|x64\r
+               {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|Mixed Platforms.Build.0 = Release|x64\r
+               {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|Win32.ActiveCfg = Release|Win32\r
+               {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|Win32.Build.0 = Release|Win32\r
+               {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|x64.ActiveCfg = Release|x64\r
+               {0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|x64.Build.0 = Release|x64\r
                {7826EA27-D4CC-4FAA-AD23-DF813823227B}.Debug|Any CPU.ActiveCfg = Debug|Win32\r
                {7826EA27-D4CC-4FAA-AD23-DF813823227B}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32\r
                {7826EA27-D4CC-4FAA-AD23-DF813823227B}.Debug|Mixed Platforms.Build.0 = Debug|Win32\r
index a2c74091a3a1a46c358a195b5e6fffd155106daa..7a1985aa20179eb4f9349f1cc62018d04f60780a 100644 (file)
@@ -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);
                }
 
index d2a9fddb9f40767b6989c1af6e8165e58b7f30b8..5416a604c6fc5c0788d14450d016daaf9c0d0ddb 100644 (file)
@@ -77,7 +77,7 @@
  */
 
 #ifndef _DNS_SD_H
-#define _DNS_SD_H 2582100
+#define _DNS_SD_H 3200500
 
 #ifdef  __cplusplus
     extern "C" {
@@ -350,12 +350,27 @@ enum
         * if this host has no routable IPv4 address, the call will not try to look up IPv4 addresses for
         * "hostname".
         */
+
+    kDNSServiceFlagsTimeout            = 0x10000,
+       /*
+        * When kDNServiceFlagsTimeout is passed to DNSServiceQueryRecord or DNSServiceGetAddrInfo, the query is
+        * stopped after a certain number of seconds have elapsed. The time at which the query will be stopped
+        * is determined by the system and cannot be configured by the user. The query will be stopped irrespective
+        * of whether a response was given earlier or not. When the query is stopped, the callback will be called
+        * with an error code of kDNSServiceErr_Timeout and a NULL sockaddr will be returned for DNSServiceGetAddrInfo
+        * and zero length rdata will be returned for DNSServiceQueryRecord.
+        */
+
+    kDNSServiceFlagsIncludeP2P          = 0x20000,
+       /*
+        * Include P2P interfaces when kDNSServiceInterfaceIndexAny is specified.
+        * By default, specifying kDNSServiceInterfaceIndexAny does not include P2P interfaces.
+        */
        kDNSServiceFlagsWakeOnResolve      = 0x40000
        /*
         * This flag is meaningful only in DNSServiceResolve. When set, it tries to send a magic packet
         * to wake up the client.
         */
-
     };
 
 /* Possible protocols for DNSServiceNATPortMappingCreate(). */
@@ -493,7 +508,8 @@ enum
     kDNSServiceErr_NATPortMappingUnsupported = -65564,  /* NAT doesn't support NAT-PMP or UPnP */
     kDNSServiceErr_NATPortMappingDisabled    = -65565,  /* NAT supports NAT-PMP or UPnP but it's disabled by the administrator */
     kDNSServiceErr_NoRouter                  = -65566,  /* No router currently configured (probably no network connectivity) */
-    kDNSServiceErr_PollingMode               = -65567
+    kDNSServiceErr_PollingMode               = -65567,
+    kDNSServiceErr_Timeout                   = -65568
 
     /* mDNS Error codes are in the range
      * FFFE FF00 (-65792) to FFFE FFFF (-65537) */
@@ -604,10 +620,10 @@ enum
  *   interface via which the service can be accessed.
  *
  * If applications pass kDNSServiceInterfaceIndexAny to DNSServiceBrowse
- * or DNSServiceQueryRecord, the operation will also include P2P. In this
- * case, if a service instance or the record being queried is found over P2P,
- * the resulting ADD event will indicate kDNSServiceInterfaceIndexP2P as the
- * interface index.
+ * or DNSServiceQueryRecord, they must set the kDNSServiceFlagsIncludeP2P flag
+ * to include P2P. In this case, if a service instance or the record being queried 
+ * is found over P2P, the resulting ADD event will indicate kDNSServiceInterfaceIndexP2P 
+ * as the interface index.
  */
 
 #define kDNSServiceInterfaceIndexAny 0
index ecce4fc23cd32cdb610525509343837ffb111423..cba52d32bca0fe2908d81ee89820d398f8650960 100644 (file)
@@ -885,7 +885,7 @@ mDNSlocal mDNSu8 *PutUpdateSRV(DaemonInfo *d, DNSZone * zone, PktMsg *pkt, mDNSu
        
        ( void ) d;
 
-       mDNS_SetupResourceRecord(&rr, NULL, 0, kDNSType_SRV, SRV_TTL, kDNSRecordTypeUnique, NULL, NULL);
+       mDNS_SetupResourceRecord(&rr, NULL, 0, kDNSType_SRV, SRV_TTL, kDNSRecordTypeUnique, AuthRecordAny, NULL, NULL);
        rr.resrec.rrclass = kDNSClass_IN;
        rr.resrec.rdata->u.srv.priority = 0;
        rr.resrec.rdata->u.srv.weight   = 0;
@@ -1645,7 +1645,7 @@ exit:
 mDNSlocal void FormatLLQOpt(AuthRecord *opt, int opcode, const mDNSOpaque64 *const id, mDNSs32 lease)
        {
        mDNSPlatformMemZero(opt, sizeof(*opt));
-       mDNS_SetupResourceRecord(opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
+       mDNS_SetupResourceRecord(opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
        opt->resrec.rrclass = NormalMaxDNSMessageData;
        opt->resrec.rdlength   = sizeof(rdataOPT);      // One option in this OPT record
        opt->resrec.rdestimate = sizeof(rdataOPT);
@@ -3104,9 +3104,9 @@ void mDNSCoreReceive(mDNS *const m, void *const msg, const mDNSu8 *const end,
                                 const mDNSAddr *const srcaddr, const mDNSIPPort srcport,
                                 const mDNSAddr *const dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID iid)
        { ( void ) m; ( void ) msg; ( void ) end; ( void ) srcaddr; ( void ) srcport; ( void ) dstaddr; ( void ) dstport; ( void ) iid; }
-DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped)
-       { ( void ) m; ( void ) d; ( void ) interface; ( void ) addr; ( void ) port; ( void ) scoped; return(NULL); }
-void mDNS_AddSearchDomain(const domainname *const domain) { (void)domain; }
+DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout)
+       { ( void ) m; ( void ) d; ( void ) interface; ( void ) addr; ( void ) port; ( void ) scoped; ( void ) timeout; return(NULL); }
+void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID) { (void)domain; (void) InterfaceID;}
 void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext)
        { ( void ) m; ( void ) fqdn; ( void ) StatusCallback; ( void ) StatusContext; }
 mDNSs32 mDNS_Execute   (mDNS *const m) { ( void ) m; return 0; }
@@ -3127,8 +3127,8 @@ void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr,  const mDNSAd
        { ( void ) m; ( void ) v4addr; ( void ) v6addr; ( void ) router; }
 mStatus uDNS_SetupDNSConfig( mDNS *const m ) { ( void ) m; return 0; }
 mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info,
-       const domainname *domain, const domainname *keyname, const char *b64keydata, mDNSBool AutoTunnel)
-       { ( void ) m; ( void ) info; ( void ) domain; ( void ) keyname; ( void ) b64keydata; ( void ) AutoTunnel; return 0; }
+       const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, const char *autoTunnelPrefix)
+       { ( void ) m; ( void ) info; ( void ) domain; ( void ) keyname; ( void ) b64keydata; ( void ) hostname; (void) port; ( void ) autoTunnelPrefix; return 0; }
 mStatus mDNS_StopQuery(mDNS *const m, DNSQuestion *const question) { ( void ) m; ( void ) question; return 0; }
 void TriggerEventCompletion(void);
 void TriggerEventCompletion() {}
index a891915d6dae830b2e041006338c38a4b8a81882..3a4c6296706ed75b4ae919c04bc36e2162b01ab5 100644 (file)
@@ -264,7 +264,7 @@ DNSServiceErrorType DNSServiceRegister
                txtRecord, txtLen,              // TXT data, length
                SubTypes, NumSubTypes,  // Subtypes
                mDNSInterface_Any,              // Interface ID
-               RegCallback, x);                // Callback and context
+               RegCallback, x, 0);             // Callback, context, flags
        if (err) { mDNSPlatformMemFree(x); errormsg = "mDNS_RegisterService"; goto fail; }
 
        // Succeeded: Wrap up and return
@@ -509,6 +509,13 @@ DNSServiceErrorType DNSServiceResolve
        x->qSRV.ExpectUnique        = mDNStrue;
        x->qSRV.ForceMCast          = mDNSfalse;
        x->qSRV.ReturnIntermed      = mDNSfalse;
+       x->qSRV.SuppressUnusable    = mDNSfalse;
+       x->qSRV.SearchListIndex     = 0;
+       x->qSRV.AppendSearchDomains = 0;
+       x->qSRV.RetryWithSearchDomains = mDNSfalse;
+       x->qSRV.TimeoutQuestion     = 0;
+       x->qSRV.WakeOnResolve       = 0;
+       x->qSRV.qnameOrig           = mDNSNULL;
        x->qSRV.QuestionCallback    = FoundServiceInfo;
        x->qSRV.QuestionContext     = x;
 
@@ -523,7 +530,12 @@ DNSServiceErrorType DNSServiceResolve
        x->qTXT.ForceMCast          = mDNSfalse;
        x->qTXT.ReturnIntermed      = mDNSfalse;
        x->qTXT.SuppressUnusable    = mDNSfalse;
-       x->qTXT.WakeOnResolve       = mDNSfalse;
+       x->qTXT.SearchListIndex     = 0;
+       x->qTXT.AppendSearchDomains = 0;
+       x->qTXT.RetryWithSearchDomains = mDNSfalse;
+       x->qTXT.TimeoutQuestion     = 0;
+       x->qTXT.WakeOnResolve       = 0;
+       x->qTXT.qnameOrig           = mDNSNULL;
        x->qTXT.QuestionCallback    = FoundServiceInfo;
        x->qTXT.QuestionContext     = x;
 
@@ -648,6 +660,11 @@ DNSServiceErrorType DNSServiceQueryRecord
        x->q.ForceMCast          = (flags & kDNSServiceFlagsForceMulticast) != 0;
        x->q.ReturnIntermed      = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
        x->q.SuppressUnsable     = (flags & kDNSServiceFlagsSuppressUnusable) != 0;
+       x->q.SearchListIndex     = 0;
+       x->q.AppendSearchDomains = 0;
+       x->q.RetryWithSearchDomains = mDNSfalse;
+       x->q.WakeOnResolve       = 0;
+       x->q.qnameOrig           = mDNSNULL;
        x->q.QuestionCallback    = DNSServiceQueryRecordResponse;
        x->q.QuestionContext     = x;
 
index fc4373019725bf392444fcc78e725ac9818a280f..af182f7a4c9736adf28a20affaded60727d8808d 100644 (file)
@@ -31,6 +31,8 @@
 
 #include "dnssd_ipc.h"
 
+static int gDaemonErr = kDNSServiceErr_NoError;
+
 #if defined(_WIN32)
 
        #define _SSIZE_T
@@ -56,6 +58,7 @@
        
        static int g_initWinsock = 0;
        #define LOG_WARNING kDebugLevelWarning
+       #define LOG_INFO kDebugLevelInfo
        static void syslog( int priority, const char * message, ...)
                {
                va_list args;
@@ -88,6 +91,8 @@
 // Uncomment the line below to use the old error return mechanism of creating a temporary named socket (e.g. in /var/tmp)
 //#define USE_NAMED_ERROR_RETURN_SOCKET 1
 
+#define DNSSD_CLIENT_TIMEOUT 10  // In seconds
+
 #ifndef CTL_PATH_PREFIX
 #define CTL_PATH_PREFIX "/var/tmp/dnssd_result_socket."
 #endif
@@ -196,6 +201,9 @@ static int read_all(dnssd_sock_t sd, char *buf, int len)
        while (len)
                {
                ssize_t num_read = recv(sd, buf, len, 0);
+               // It is valid to get an interrupted system call error e.g., somebody attaching
+               // in a debugger, retry without failing
+               if ((num_read < 0) && (errno == EINTR)) { syslog(LOG_INFO, "dnssd_clientstub read_all: EINTR continue"); continue; }
                if ((num_read == 0) || (num_read < 0) || (num_read > len))
                        {
                        int printWarn = 0;
@@ -262,6 +270,39 @@ static int more_bytes(dnssd_sock_t sd)
        return (ret > 0);
        }
 
+// Wait for daemon to write to socket
+static int wait_for_daemon(dnssd_sock_t sock, int timeout)
+       {
+#ifndef WIN32
+       // At this point the next operation (accept() or read()) on this socket may block for a few milliseconds waiting
+       // for the daemon to respond, but that's okay -- the daemon is a trusted service and we know if won't take more
+       // than a few milliseconds to respond.  So we'll forego checking for readability of the socket.
+       (void) sock;
+       (void) timeout;
+#else
+       // Windows on the other hand suffers from 3rd party software (primarily 3rd party firewall software) that
+       // interferes with proper functioning of the TCP protocol stack. Because of this and because we depend on TCP
+       // to communicate with the system service, we want to make sure that the next operation on this socket (accept() or
+       // read()) doesn't block indefinitely.
+       if (!gDaemonErr)
+               {
+               struct timeval tv;
+               fd_set set;
+
+               FD_ZERO(&set);
+               FD_SET(sock, &set);
+               tv.tv_sec = timeout;
+               tv.tv_usec = 0;
+               if (!select((int)(sock + 1), &set, NULL, NULL, &tv))
+                       {
+                               syslog(LOG_WARNING, "dnssd_clientstub wait_for_daemon timed out");
+                               gDaemonErr = kDNSServiceErr_Timeout;
+                       }
+               }
+#endif
+       return gDaemonErr;
+       }
+
 /* create_hdr
  *
  * allocate and initialize an ipc message header. Value of len should initially be the
@@ -353,7 +394,6 @@ static void FreeDNSServiceOp(DNSServiceOp *x)
                x->ProcessReply = NULL;
                x->AppCallback  = NULL;
                x->AppContext   = NULL;
-               x->rec          = NULL;
 #if _DNS_SD_LIBDISPATCH
                if (x->disp_source)     dispatch_release(x->disp_source);
                x->disp_source  = NULL;
@@ -612,7 +652,10 @@ static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr)
 #else
        if (write_all(sdr->sockfd, (char *)hdr, datalen + sizeof(ipc_msg_hdr)) < 0)
                {
-               syslog(LOG_WARNING, "dnssd_clientstub deliver_request ERROR: write_all(%d, %lu bytes) failed",
+               // write_all already prints an error message if there is an error writing to
+               // the socket except for DEFUNCT. Logging here is unnecessary and also wrong
+               // in the case of DEFUNCT sockets
+               syslog(LOG_INFO, "dnssd_clientstub deliver_request ERROR: write_all(%d, %lu bytes) failed",
                        sdr->sockfd, (unsigned long)(datalen + sizeof(ipc_msg_hdr)));
                goto cleanup;
                }
@@ -626,6 +669,7 @@ static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr)
                // but that's okay -- the daemon is a trusted service and we know if won't take more than a few milliseconds to respond.
                dnssd_sockaddr_t daddr;
                dnssd_socklen_t len = sizeof(daddr);
+               if ((err = wait_for_daemon(listenfd, DNSSD_CLIENT_TIMEOUT)) != kDNSServiceErr_NoError) goto cleanup;
                errsd = accept(listenfd, (struct sockaddr *)&daddr, &len);
                if (!dnssd_SocketValid(errsd)) deliver_request_bailout("accept");
 #else
@@ -711,10 +755,13 @@ static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr)
        // but that's okay -- the daemon is a trusted service and we know if won't take more than a few milliseconds to respond.
        if (sdr->op == send_bpf)        // Okay to use sdr->op when checking for op == send_bpf
                err = kDNSServiceErr_NoError;
-       else if (read_all(errsd, (char*)&err, (int)sizeof(err)) < 0)
-               err = kDNSServiceErr_ServiceNotRunning; // On failure read_all will have written a message to syslog for us
-       else
-               err = ntohl(err);
+       else if ((err = wait_for_daemon(errsd, DNSSD_CLIENT_TIMEOUT)) == kDNSServiceErr_NoError)
+               {
+               if (read_all(errsd, (char*)&err, (int)sizeof(err)) < 0)
+                       err = kDNSServiceErr_ServiceNotRunning; // On failure read_all will have written a message to syslog for us
+               else
+                       err = ntohl(err);
+               }
 
        //syslog(LOG_WARNING, "dnssd_clientstub deliver_request: retrieved error code %d", err);
 
@@ -1103,7 +1150,7 @@ DNSServiceErrorType DNSSD_API DNSServiceResolve
                {
                return kDNSServiceErr_BadParam;
                }
-
+               
        err = ConnectToServer(sdRef, flags, resolve_request, handle_resolve_response, callBack, context);
        if (err) return err;    // On error ConnectToServer leaves *sdRef set to NULL
 
index 760e1b4bc7c777da8115efe2825f46c801dee55c..d41edc8725257619f6a38475a6a3a1d739ede9fc 100644 (file)
@@ -40,6 +40,7 @@
 #      define dnssd_SocketValid(s) ((s) != INVALID_SOCKET)
 #      define dnssd_EWOULDBLOCK        WSAEWOULDBLOCK
 #      define dnssd_EINTR                      WSAEINTR
+#      define dnssd_ECONNRESET         WSAECONNRESET
 #      define dnssd_sock_t                     SOCKET
 #      define dnssd_socklen_t          int
 #      define dnssd_close(sock)        closesocket(sock)
@@ -63,6 +64,7 @@ extern char *win32_strerror(int inErrorCode);
 #      define dnssd_SocketValid(s) ((s) >= 0)
 #      define dnssd_EWOULDBLOCK        EWOULDBLOCK
 #      define dnssd_EINTR                      EINTR
+#      define dnssd_ECONNRESET         ECONNRESET
 #      define dnssd_EPIPE                      EPIPE
 #      define dnssd_sock_t                     int
 #      define dnssd_socklen_t          unsigned int
@@ -116,7 +118,7 @@ extern char *win32_strerror(int inErrorCode);
 typedef enum
     {
     request_op_none = 0,       // No request yet received on this connection
-    connection_request = 1,    // connected socket via DNSServiceCreateConnection()
+    connection_request = 1,    // connected socket via DNSServiceConnect()
     reg_record_request,                // reg/remove record only valid for connected sockets
     remove_record_request,
     enumeration_request,
index 9ad7bc5db4f2cd051205078b9210ecaa750d91ea..a2588e7d01a9571be706c4db6426175ff0a99961 100644 (file)
 #include "uDNS.h"
 #include "uds_daemon.h"
 
+// Normally we append search domains only for queries with a single label that are not
+// fully qualified. This can be overridden to apply search domains for queries (that are
+// not fully qualified) with any number of labels e.g., moon, moon.cs, moon.cs.be, etc.
+mDNSBool AlwaysAppendSearchDomains = mDNSfalse;
+
 // Apple-specific functionality, not required for other platforms
 #if APPLE_OSX_mDNSResponder
 #include <sys/ucred.h>
@@ -145,6 +150,7 @@ struct request_state
        int unresponsiveness_reports;
        struct reply_state *replies;    // corresponding (active) reply list
        req_termination_fn terminate;
+       DNSServiceFlags         flags;  
 
        union
                {
@@ -180,7 +186,9 @@ struct request_state
                        mDNSu32              flags;
                        mDNSu32              protocol;
                        DNSQuestion          q4;
+                       DNSQuestion          *q42;
                        DNSQuestion          q6;
+                       DNSQuestion          *q62;
                        } addrinfo;
                struct
                        {
@@ -198,7 +206,7 @@ struct request_state
                struct
                        {
                        DNSQuestion q;
-                       DNSQuestion q2;
+                       DNSQuestion *q2;
                        } queryrecord;
                struct
                        {
@@ -415,7 +423,7 @@ mDNSlocal mStatus GenerateNTDResponse(const domainname *const servicename, const
                // Build reply header
                *rep = create_reply(op, len, request);
                (*rep)->rhdr->flags = dnssd_htonl(flags);
-               (*rep)->rhdr->ifi   = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id));
+               (*rep)->rhdr->ifi   = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id, mDNSfalse));
                (*rep)->rhdr->error = dnssd_htonl(err);
 
                // Build reply body
@@ -458,7 +466,7 @@ mDNSlocal void GenerateBonjourBrowserResponse(const domainname *const servicenam
        // Build reply header
        *rep = create_reply(op, len, request);
        (*rep)->rhdr->flags = dnssd_htonl(flags);
-       (*rep)->rhdr->ifi   = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id));
+       (*rep)->rhdr->ifi   = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id, mDNSfalse));
        (*rep)->rhdr->error = dnssd_htonl(err);
 
        // Build reply body
@@ -484,6 +492,10 @@ mDNSlocal AuthRecord *read_rr_from_ipc_msg(request_state *request, int GetTTL, i
        mDNSu32 ttl   = GetTTL ? get_uint32(&request->msgptr, request->msgend) : 0;
        int storage_size = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody);
        AuthRecord *rr;
+       mDNSInterfaceID InterfaceID;
+       AuthRecType artype;
+
+       request->flags = flags;
 
        if (str_err) { LogMsg("ERROR: read_rr_from_ipc_msg - get_string"); return NULL; }
 
@@ -499,8 +511,19 @@ mDNSlocal AuthRecord *read_rr_from_ipc_msg(request_state *request, int GetTTL, i
 
        rr = mallocL("AuthRecord/read_rr_from_ipc_msg", sizeof(AuthRecord) - sizeof(RDataBody) + storage_size);
        if (!rr) FatalError("ERROR: malloc");
-       mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex),
-               type, 0, (mDNSu8) ((flags & kDNSServiceFlagsShared) ? kDNSRecordTypeShared : kDNSRecordTypeUnique), mDNSNULL, mDNSNULL);
+
+       InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+       if (InterfaceID == mDNSInterface_LocalOnly)
+               artype = AuthRecordLocalOnly;
+       else if (InterfaceID == mDNSInterface_P2P)
+               artype = AuthRecordP2P;
+       else if ((InterfaceID == mDNSInterface_Any) && (flags & kDNSServiceFlagsIncludeP2P))
+               artype = AuthRecordAnyIncludeP2P;
+       else
+               artype = AuthRecordAny;
+
+       mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, type, 0,
+               (mDNSu8) ((flags & kDNSServiceFlagsShared) ? kDNSRecordTypeShared : kDNSRecordTypeUnique), artype, mDNSNULL, mDNSNULL);
 
        if (!MakeDomainNameFromDNSNameString(&rr->namestorage, name))
                {
@@ -590,6 +613,14 @@ mDNSlocal void external_start_advertising_helper(service_instance *const instanc
                return;
                }
 
+#if APPLE_OSX_mDNSResponder
+       // Update packet filter if p2p interface already exists, otherwise,
+       // if will be updated when we get the KEV_DL_IF_ATTACHED event for
+       // the interface.  Called here since we don't call external_start_advertising_service()
+       // with the SRV record when advertising a service.
+       mDNSInitPacketFilter();
+#endif // APPLE_OSX_mDNSResponder
+
        if (instance->external_advertise) LogMsg("external_start_advertising_helper: external_advertise already set!");
        
        for ( i = 0; i < instance->request->u.servicereg.num_subtypes; i++)
@@ -611,9 +642,11 @@ mDNSlocal void external_stop_advertising_helper(service_instance *const instance
        int i;
        
        if (!instance->external_advertise) return;
+
+       LogInfo("external_stop_advertising_helper: calling external_stop_advertising_service");
        
        for ( i = 0; i < instance->request->u.servicereg.num_subtypes; i++)
-               external_start_advertising_service(&st[i].resrec);
+               external_stop_advertising_service(&st[i].resrec);
        
        external_stop_advertising_service(&instance->srs.RR_PTR.resrec);
        external_stop_advertising_service(&instance->srs.RR_TXT.resrec);
@@ -766,8 +799,11 @@ mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, m
                        LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c);
                else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; }
 
-               if (instance->request->u.servicereg.InterfaceID == mDNSInterface_P2P || (!instance->request->u.servicereg.InterfaceID && SameDomainName(&instance->domain, &localdomain)))
+               if (instance->request->u.servicereg.InterfaceID == mDNSInterface_P2P || (!instance->request->u.servicereg.InterfaceID && SameDomainName(&instance->domain, &localdomain) && (instance->request->flags & kDNSServiceFlagsIncludeP2P)))
+                       {
+                       LogInfo("regservice_callback: calling external_start_advertising_helper()");
                        external_start_advertising_helper(instance);
+                       }
                if (instance->request->u.servicereg.autoname && CountPeerRegistrations(m, srs) == 0)
                        RecordUpdatedNiceLabel(m, 0);   // Successfully got new name, tell user immediately
                }
@@ -833,6 +869,12 @@ mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord *rr, mStatus result)
                else
                        {
                        if (result != mStatus_MemFree) LogMsg("regrecord_callback: error %d received after parent termination", result);
+
+                       // We come here when the record is being deregistered either from DNSServiceRemoveRecord or connection_termination.
+                       // If the record has been updated, we need to free the rdata. Everytime we call mDNS_Update, it calls update_callback
+                       // with the old rdata (so that we can free it) and stores the new rdata in "rr->resrec.rdata". This means, we need
+                       // to free the latest rdata for which the update_callback was never called with.
+                       if (rr->resrec.rdata != &rr->rdatastorage) freeL("RData/regrecord_callback", rr->resrec.rdata);
                        freeL("AuthRecord/regrecord_callback", rr);
                        }
                }
@@ -856,7 +898,7 @@ mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord *rr, mStatus result)
                        reply_state *reply = create_reply(reg_record_reply_op, len, request);
                        reply->mhdr->client_context = re->regrec_client_context;
                        reply->rhdr->flags = dnssd_htonl(0);
-                       reply->rhdr->ifi   = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, rr->resrec.InterfaceID));
+                       reply->rhdr->ifi   = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, rr->resrec.InterfaceID, mDNSfalse));
                        reply->rhdr->error = dnssd_htonl(result);
                        append_reply(request, reply);
                        }
@@ -874,8 +916,10 @@ mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord *rr, mStatus result)
                else
                        {
                        if (re->external_advertise) LogMsg("regrecord_callback: external_advertise already set!");
-                       if (re->origInterfaceID == mDNSInterface_P2P || (!re->origInterfaceID && IsLocalDomain(&rr->namestorage)))
+
+                       if (re->origInterfaceID == mDNSInterface_P2P || (!re->origInterfaceID && IsLocalDomain(&rr->namestorage) && (request->flags & kDNSServiceFlagsIncludeP2P)))
                                {
+                               LogInfo("regrecord_callback: calling external_start_advertising_service");
                                external_start_advertising_service(&rr->resrec);
                                re->external_advertise = mDNStrue;
                                }
@@ -950,8 +994,18 @@ mDNSlocal mStatus handle_regrecord_request(request_state *request)
        AuthRecord *rr = read_rr_from_ipc_msg(request, 1, 1);
        if (rr)
                {
+               registered_record_entry *re;
+               // Don't allow non-local domains to be regsitered as LocalOnly. Allowing this would permit
+               // clients to register records such as www.bigbank.com A w.x.y.z to redirect Safari.
+               if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly && !IsLocalDomain(rr->resrec.name) &&
+                       rr->resrec.rrclass == kDNSClass_IN && (rr->resrec.rrtype == kDNSType_A || rr->resrec.rrtype == kDNSType_AAAA ||
+                       rr->resrec.rrtype == kDNSType_CNAME))
+                       {
+                       freeL("AuthRecord/handle_regrecord_request", rr);
+                       return (mStatus_BadParamErr);
+                       }
                // allocate registration entry, link into list
-               registered_record_entry *re = mallocL("registered_record_entry", sizeof(registered_record_entry));
+               re = mallocL("registered_record_entry", sizeof(registered_record_entry));
                if (!re) FatalError("ERROR: malloc");
                re->key                   = request->hdr.reg_index;
                re->rr                    = rr;
@@ -961,9 +1015,6 @@ mDNSlocal mStatus handle_regrecord_request(request_state *request)
                rr->RecordContext         = re;
                rr->RecordCallback        = regrecord_callback;
 
-               re->next = request->u.reg_recs;
-               request->u.reg_recs = re;
-
                re->origInterfaceID = rr->resrec.InterfaceID;
                if (rr->resrec.InterfaceID == mDNSInterface_P2P) rr->resrec.InterfaceID = mDNSInterface_Any;    
 #if 0
@@ -974,6 +1025,17 @@ mDNSlocal mStatus handle_regrecord_request(request_state *request)
        
                LogOperation("%3d: DNSServiceRegisterRecord(%u %s) START", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec));
                err = mDNS_Register(&mDNSStorage, rr);
+               if (err)
+                       {
+                       LogOperation("%3d: DNSServiceRegisterRecord(%u %s) ERROR (%d)", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec), err);
+                       freeL("registered_record_entry", re);
+                       freeL("registered_record_entry/AuthRecord", rr);
+                       }
+               else
+                       {
+                       re->next = request->u.reg_recs;
+                       request->u.reg_recs = re;
+                       }
                }
        return(err);
        }
@@ -1036,12 +1098,16 @@ mDNSlocal mStatus add_record_to_service(request_state *request, service_instance
        extra->r.resrec.rdlength = rdlen;
        mDNSPlatformMemCopy(&extra->r.rdatastorage.u.data, rdata, rdlen);
 
-       result = mDNS_AddRecordToService(&mDNSStorage, srs, extra, &extra->r.rdatastorage, ttl);
+       result = mDNS_AddRecordToService(&mDNSStorage, srs, extra, &extra->r.rdatastorage, ttl,
+                                       (request->flags & kDNSServiceFlagsIncludeP2P) ? 1: 0);
        if (result) { freeL("ExtraResourceRecord/add_record_to_service", extra); return result; }
 
        extra->ClientID = request->hdr.reg_index;
-       if (instance->external_advertise && (instance->request->u.servicereg.InterfaceID == mDNSInterface_P2P || (!instance->request->u.servicereg.InterfaceID && SameDomainName(&instance->domain, &localdomain))))
+       if (instance->external_advertise && (instance->request->u.servicereg.InterfaceID == mDNSInterface_P2P || (!instance->request->u.servicereg.InterfaceID && SameDomainName(&instance->domain, &localdomain) && (instance->request->flags & kDNSServiceFlagsIncludeP2P))))
+               {
+               LogInfo("add_record_to_service: calling external_start_advertising_service");
                external_start_advertising_service(&extra->r.resrec);
+               }
        return result;
        }
 
@@ -1065,7 +1131,12 @@ mDNSlocal mStatus handle_add_request(request_state *request)
        if (request->terminate != regservice_termination_callback)
                { LogMsg("%3d: DNSServiceAddRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); }
 
-       LogOperation("%3d: DNSServiceAddRecord(%##s, %s, %d)", request->sd,
+       // For a service registered with zero port, don't allow adding records. This mostly happens due to a bug
+       // in the application. See radar://9165807.
+       if (mDNSIPPortIsZero(request->u.servicereg.port))
+               { LogMsg("%3d: DNSServiceAddRecord: adding record to a service registered with zero port", request->sd); return(mStatus_BadParamErr); }
+
+       LogOperation("%3d: DNSServiceAddRecord(%X, %##s, %s, %d)", request->sd, flags,
                (request->u.servicereg.instances) ? request->u.servicereg.instances->srs.RR_SRV.resrec.name->c : NULL, DNSTypeName(rrtype), rdlen);
 
        for (i = request->u.servicereg.instances; i; i = i->next)
@@ -1098,6 +1169,7 @@ mDNSlocal void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd
                if (ext.rdlength == oldrdlen && mDNSPlatformMemSame(&ext.rdata->u, &oldrd->u, oldrdlen)) goto exit;
                SetNewRData(&ext, oldrd, oldrdlen);
                external_stop_advertising_service(&ext);
+               LogInfo("update_callback: calling external_start_advertising_service");
                external_start_advertising_service(&rr->resrec);
                }
 exit:
@@ -1165,6 +1237,10 @@ mDNSlocal mStatus handle_update_request(request_state *request)
        if (request->terminate != regservice_termination_callback)
                { LogMsg("%3d: DNSServiceUpdateRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); }
 
+       // For a service registered with zero port, only SRV record is initialized. Don't allow any updates.
+       if (mDNSIPPortIsZero(request->u.servicereg.port))
+               { LogMsg("%3d: DNSServiceUpdateRecord: updating the record of a service registered with zero port", request->sd); return(mStatus_BadParamErr); }
+
        // update the saved off TXT data for the service
        if (hdr->reg_index == TXT_RECORD_INDEX)
                {
@@ -1345,7 +1421,7 @@ mDNSexport AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p)
                if (!st) return(mDNSNULL);
                for (i = 0; i < NumSubTypes; i++)
                        {
-                       mDNS_SetupResourceRecord(&st[i], mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, mDNSNULL, mDNSNULL);
+                       mDNS_SetupResourceRecord(&st[i], mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, AuthRecordAny, mDNSNULL, mDNSNULL);
                        while (*p) p++;
                        p++;
                        if (!MakeDomainNameFromDNSNameString(&st[i].namestorage, p))
@@ -1362,8 +1438,19 @@ mDNSlocal mStatus register_service_instance(request_state *request, const domain
        const mDNSBool DomainIsLocal = SameDomainName(domain, &localdomain);
        mStatus result;
        mDNSInterfaceID interfaceID = request->u.servicereg.InterfaceID;
+       mDNSu32 regFlags = 0;
+
+       if (interfaceID == mDNSInterface_P2P)
+               {
+               interfaceID = mDNSInterface_Any;
+               regFlags |= regFlagIncludeP2P;
+               }
+       else if (request->flags & kDNSServiceFlagsIncludeP2P)
+               regFlags |= regFlagIncludeP2P;
 
-       if (interfaceID == mDNSInterface_P2P) interfaceID = mDNSInterface_Any;
+       // client guarantees that record names are unique
+       if (request->flags & kDNSServiceFlagsForce)
+               regFlags |= regFlagKnownUnique;
 
        // If the client specified an interface, but no domain, then we honor the specified interface for the "local" (mDNS)
        // registration but for the wide-area registrations we don't (currently) have any concept of a wide-area unicast
@@ -1415,7 +1502,7 @@ mDNSlocal mStatus register_service_instance(request_state *request, const domain
                request->u.servicereg.port,
                request->u.servicereg.txtdata, request->u.servicereg.txtlen,
                instance->subtypes, request->u.servicereg.num_subtypes,
-               interfaceID, regservice_callback, instance);
+               interfaceID, regservice_callback, instance, regFlags);
 
        if (!result)
                {
@@ -1516,6 +1603,7 @@ mDNSlocal mStatus handle_regservice_request(request_state *request)
                get_string(&request->msgptr, request->msgend, host, MAX_ESCAPED_DOMAIN_NAME) < 0)
                { LogMsg("ERROR: handle_regservice_request - Couldn't read name/regtype/domain"); return(mStatus_BadParamErr); }
 
+       request->flags = flags;
        request->u.servicereg.InterfaceID = InterfaceID;
        request->u.servicereg.instances = NULL;
        request->u.servicereg.txtlen  = 0;
@@ -1600,8 +1688,8 @@ mDNSlocal mStatus handle_regservice_request(request_state *request)
                                count+1, srv.c, mDNSVal16(request->u.servicereg.port));
                }
 
-       LogOperation("%3d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", \"%s\", %u) START",
-               request->sd, name, request->u.servicereg.type_as_string, domain, host, mDNSVal16(request->u.servicereg.port));
+       LogOperation("%3d: DNSServiceRegister(%X, %d, \"%s\", \"%s\", \"%s\", \"%s\", %u) START",
+               request->sd, flags, interfaceIndex, name, request->u.servicereg.type_as_string, domain, host, mDNSVal16(request->u.servicereg.port));
 
        // We need to unconditionally set request->terminate, because even if we didn't successfully
        // start any registrations right now, subsequent configuration changes may cause successful
@@ -1668,7 +1756,7 @@ bonjourbrowserhack:
 
        LogOperation("%3d: DNSServiceBrowse(%##s, %s) RESULT %s %d: %s",
                req->sd, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "Add" : "Rmv",
-               mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID), RRDisplayString(m, answer));
+               mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNSfalse), RRDisplayString(m, answer));
 
        append_reply(req, rep);
        }
@@ -1699,10 +1787,11 @@ mDNSlocal mStatus add_domain_to_browser(request_state *info, const domainname *d
                b->next = info->u.browser.browsers;
                info->u.browser.browsers = b;
                LogOperation("%3d: DNSServiceBrowse(%##s) START", info->sd, b->q.qname.c);
-               if (info->u.browser.interface_id == mDNSInterface_P2P || (!info->u.browser.interface_id && SameDomainName(&b->domain, &localdomain)))
+               if (info->u.browser.interface_id == mDNSInterface_P2P || (!info->u.browser.interface_id && SameDomainName(&b->domain, &localdomain) && (info->flags & kDNSServiceFlagsIncludeP2P)))
                        {
                        domainname tmp;
                        ConstructServiceName(&tmp, NULL, &info->u.browser.regtype, &b->domain);
+                       LogInfo("add_domain_to_browser: calling external_start_browsing_for_service()");
                        external_start_browsing_for_service(&mDNSStorage, &tmp, kDNSType_PTR);
                        }
                }
@@ -1715,10 +1804,11 @@ mDNSlocal void browse_termination_callback(request_state *info)
                {
                browser_t *ptr = info->u.browser.browsers;
                
-               if (info->u.browser.interface_id == mDNSInterface_P2P || (!info->u.browser.interface_id && SameDomainName(&ptr->domain, &localdomain)))
+               if (info->u.browser.interface_id == mDNSInterface_P2P || (!info->u.browser.interface_id && SameDomainName(&ptr->domain, &localdomain) && (info->flags & kDNSServiceFlagsIncludeP2P)))
                        {
                        domainname tmp;
                        ConstructServiceName(&tmp, NULL, &info->u.browser.regtype, &ptr->domain);
+                       LogInfo("browse_termination_callback: calling external_stop_browsing_for_service()");
                        external_stop_browsing_for_service(&mDNSStorage, &tmp, kDNSType_PTR);
                        }
                
@@ -1808,7 +1898,7 @@ mDNSlocal void RegisterLocalOnlyDomainEnumPTR(mDNS *m, const domainname *d, int
                (type == mDNS_DomainTypeRegistration   ) ? "registration dom" :
                (type == mDNS_DomainTypeBrowseAutomatic) ? "automatic browse" : "?", d->c);
 
-       mDNS_SetupResourceRecord(&ptr->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, FreeARElemCallback, ptr);
+       mDNS_SetupResourceRecord(&ptr->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, AuthRecordLocalOnly, FreeARElemCallback, ptr);
        MakeDomainNameFromDNSNameString(&ptr->ar.namestorage, mDNS_DomainTypeNames[type]);
        AppendDNSNameString            (&ptr->ar.namestorage, "local");
        AssignDomainName(&ptr->ar.resrec.rdata->u.name, d);
@@ -1915,7 +2005,7 @@ mDNSlocal void UpdateDeviceInfoRecord(mDNS *const m)
                if (num_autoname > 0)
                        {
                        mDNSu8 len = m->HIHardware.c[0] < 255 - 6 ? m->HIHardware.c[0] : 255 - 6;
-                       mDNS_SetupResourceRecord(&m->DeviceInfo, mDNSNULL, mDNSNULL, kDNSType_TXT, kStandardTTL, kDNSRecordTypeAdvisory, mDNSNULL, mDNSNULL);
+                       mDNS_SetupResourceRecord(&m->DeviceInfo, mDNSNULL, mDNSNULL, kDNSType_TXT, kStandardTTL, kDNSRecordTypeAdvisory, AuthRecordAny, mDNSNULL, mDNSNULL);
                        ConstructServiceName(&m->DeviceInfo.namestorage, &m->nicelabel, &DeviceInfoName, &localdomain);
                        mDNSPlatformMemCopy(m->DeviceInfo.resrec.rdata->u.data + 1, "model=", 6);
                        mDNSPlatformMemCopy(m->DeviceInfo.resrec.rdata->u.data + 7, m->HIHardware.c + 1, len);
@@ -2042,6 +2132,7 @@ mDNSlocal mStatus handle_browse_request(request_state *request)
 
        if (domain[0] == '\0') uDNS_SetupSearchDomains(&mDNSStorage, UDNS_START_WAB_QUERY);
 
+       request->flags = flags;
        typedn.c[0] = 0;
        NumSubTypes = ChopSubTypes(regtype);    // Note: Modifies regtype string to remove trailing subtypes
        if (NumSubTypes < 0 || NumSubTypes > 1) return(mStatus_BadParamErr);
@@ -2060,7 +2151,8 @@ mDNSlocal mStatus handle_browse_request(request_state *request)
        request->u.browser.default_domain = !domain[0];
        request->u.browser.browsers = NULL;
 
-       LogOperation("%3d: DNSServiceBrowse(\"%##s\", \"%s\") START", request->sd, request->u.browser.regtype.c, domain);
+       LogOperation("%3d: DNSServiceBrowse(%X, %d, \"%##s\", \"%s\") START", 
+                       request->sd, request->flags, interfaceIndex, request->u.browser.regtype.c, domain);
 
        // We need to unconditionally set request->terminate, because even if we didn't successfully
        // start any browses right now, subsequent configuration changes may cause successful
@@ -2137,7 +2229,7 @@ mDNSlocal void resolve_result_callback(mDNS *const m, DNSQuestion *question, con
        // allocate/init reply header
        rep = create_reply(resolve_reply_op, len, req);
        rep->rhdr->flags = dnssd_htonl(0);
-       rep->rhdr->ifi   = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID));
+       rep->rhdr->ifi   = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNSfalse));
        rep->rhdr->error = dnssd_htonl(kDNSServiceErr_NoError);
 
        data = (char *)&rep->rhdr[1];
@@ -2175,6 +2267,7 @@ mDNSlocal mStatus handle_resolve_request(request_state *request)
        mDNSBool wasP2P = (interfaceIndex == kDNSServiceInterfaceIndexP2P);
        
        
+       request->flags = flags;
        if (wasP2P) interfaceIndex = kDNSServiceInterfaceIndexAny;
        
        InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
@@ -2204,7 +2297,12 @@ mDNSlocal mStatus handle_resolve_request(request_state *request)
        request->u.resolve.qsrv.ForceMCast       = (flags & kDNSServiceFlagsForceMulticast     ) != 0;
        request->u.resolve.qsrv.ReturnIntermed   = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
        request->u.resolve.qsrv.SuppressUnusable = mDNSfalse;
-       request->u.resolve.qsrv.WakeOnResolve    = (flags & kDNSServiceFlagsWakeOnResolve      ) != 0;
+       request->u.resolve.qsrv.SearchListIndex  = 0;
+       request->u.resolve.qsrv.AppendSearchDomains = 0;
+       request->u.resolve.qsrv.RetryWithSearchDomains = mDNSfalse;
+       request->u.resolve.qsrv.TimeoutQuestion  = 0;
+       request->u.resolve.qsrv.WakeOnResolve    = (flags & kDNSServiceFlagsWakeOnResolve) != 0;
+       request->u.resolve.qsrv.qnameOrig        = mDNSNULL;
        request->u.resolve.qsrv.QuestionCallback = resolve_result_callback;
        request->u.resolve.qsrv.QuestionContext  = request;
 
@@ -2218,7 +2316,12 @@ mDNSlocal mStatus handle_resolve_request(request_state *request)
        request->u.resolve.qtxt.ForceMCast       = (flags & kDNSServiceFlagsForceMulticast     ) != 0;
        request->u.resolve.qtxt.ReturnIntermed   = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
        request->u.resolve.qtxt.SuppressUnusable = mDNSfalse;
-       request->u.resolve.qtxt.WakeOnResolve    = mDNSfalse;
+       request->u.resolve.qtxt.SearchListIndex  = 0;
+       request->u.resolve.qtxt.AppendSearchDomains = 0;
+       request->u.resolve.qtxt.RetryWithSearchDomains = mDNSfalse;
+       request->u.resolve.qtxt.TimeoutQuestion  = 0;
+       request->u.resolve.qtxt.WakeOnResolve    = 0;
+       request->u.resolve.qtxt.qnameOrig        = mDNSNULL;
        request->u.resolve.qtxt.QuestionCallback = resolve_result_callback;
        request->u.resolve.qtxt.QuestionContext  = request;
 
@@ -2241,8 +2344,12 @@ mDNSlocal mStatus handle_resolve_request(request_state *request)
                        {
                        request->terminate = resolve_termination_callback;
                        // If the user explicitly passed in P2P, we don't restrict the domain in which we resolve.
-                       if (wasP2P || (!InterfaceID && IsLocalDomain(&fqdn)))
-                               { request->u.resolve.external_advertise    = mDNStrue; external_start_resolving_service(&fqdn);}
+                       if (wasP2P || (!InterfaceID && IsLocalDomain(&fqdn) && (request->flags & kDNSServiceFlagsIncludeP2P)))
+                               { 
+                               request->u.resolve.external_advertise    = mDNStrue;
+                               LogInfo("handle_resolve_request: calling external_start_resolving_service()");
+                               external_start_resolving_service(&fqdn);
+                               }
                        }
                }
 
@@ -2260,6 +2367,238 @@ mDNSlocal mStatus handle_resolve_request(request_state *request)
 // to the mDNSCore routine) that sends results back to the client, and a termination routine that aborts
 // the mDNSCore operation if the client dies or closes its socket.
 
+// Returns -1 to tell the caller that it should not try to reissue the query anymore 
+// Returns 1 on successfully appending a search domain and the caller should reissue the new query
+// Returns 0 when there are no more search domains and the caller should reissue the query
+mDNSlocal int AppendNewSearchDomain(mDNS *const m, DNSQuestion *question)
+       {
+       domainname *sd;
+       mStatus err;
+
+       // Sanity check: The caller already checks this. We use -1 to indicate that we have searched all
+       // the domains and should try the single label query directly on the wire. 
+       if (question->SearchListIndex == -1)
+               {
+               LogMsg("AppendNewSearchDomain: question %##s (%s) SearchListIndex is -1", question->qname.c, DNSTypeName(question->qtype));
+               return -1;
+               }
+
+       if (!question->AppendSearchDomains)
+               {
+               LogMsg("AppendNewSearchDomain: question %##s (%s) AppendSearchDoamins is 0", question->qname.c, DNSTypeName(question->qtype));
+               return -1;
+               }
+
+       // Save the original name, before we modify them below.
+       if (!question->qnameOrig)
+               {
+               question->qnameOrig =  mallocL("AppendNewSearchDomain", sizeof(domainname));
+               if (!question->qnameOrig) { LogMsg("AppendNewSearchDomain: ERROR!!  malloc failure"); return -1; }
+               question->qnameOrig->c[0] = 0;
+               AssignDomainName(question->qnameOrig, &question->qname);
+               LogInfo("AppendSearchDomain: qnameOrig %##s", question->qnameOrig->c);
+               }
+
+       sd = uDNS_GetNextSearchDomain(m, question->InterfaceID, &question->SearchListIndex, !question->AppendLocalSearchDomains);
+       // We use -1 to indicate that we have searched all the domains and should try the single label
+       // query directly on the wire. uDNS_GetNextSearchDomain should never return a negative value
+       if (question->SearchListIndex == -1)
+               {
+               LogMsg("AppendNewSearchDomain: ERROR!! uDNS_GetNextSearchDomain returned -1");
+               return -1;
+               }
+
+       // Not a common case. Perhaps, we should try the next search domain if it exceeds ?
+       if (sd && (DomainNameLength(question->qnameOrig) + DomainNameLength(sd)) > MAX_DOMAIN_NAME)
+               {
+               LogMsg("AppendNewSearchDomain: ERROR!! exceeding max domain length for %##s (%s) SearchDomain %##s length %d, Question name length %d", question->qnameOrig->c, DNSTypeName(question->qtype), sd->c, DomainNameLength(question->qnameOrig), DomainNameLength(sd));
+               return -1;
+               }
+
+       // if there are no more search domains and we have already tried this question
+       // without appending search domains, then we are done.
+       if (!sd && !ApplySearchDomainsFirst(question))
+               {
+               LogInfo("AppnedNewSearchDomain: No more search domains for question with name %##s (%s), not trying anymore", question->qname.c, DNSTypeName(question->qtype));
+               return -1;
+               }
+
+       // Stop the question before changing the name as negative cache entries could be pointing at this question.
+       // Even if we don't change the question in the case of returning 0, the caller is going to restart the
+       // question.
+       err = mDNS_StopQuery(&mDNSStorage, question);
+       if (err) { LogMsg("AppendNewSearchDomain: ERROR!! %##s %s mDNS_StopQuery: %d, while retrying with search domains", question->qname.c, DNSTypeName(question->qtype), (int)err); }
+
+       AssignDomainName(&question->qname, question->qnameOrig);
+       if (sd)
+               {
+               AppendDomainName(&question->qname, sd);
+               LogInfo("AppnedNewSearchDomain: Returning question with name %##s, SearchListIndex %d", question->qname.c, question->SearchListIndex);
+               return 1;
+               }
+
+       // Try the question as single label
+       LogInfo("AppnedNewSearchDomain: No more search domains for question with name %##s (%s), trying one last time", question->qname.c, DNSTypeName(question->qtype));
+       return 0;
+       }
+
+#if APPLE_OSX_mDNSResponder
+
+mDNSlocal mDNSBool DomainInSearchList(domainname *domain)
+       {
+       const SearchListElem *s;
+       for (s=SearchList; s; s=s->next)
+               if (SameDomainName(&s->domain, domain)) return mDNStrue;
+       return mDNSfalse;
+       }
+
+// Workaround for networks using Microsoft Active Directory using "local" as a private internal
+// top-level domain
+mDNSlocal mStatus SendAdditionalQuery(DNSQuestion *q, request_state *request, mStatus err)
+       {
+       extern domainname ActiveDirectoryPrimaryDomain;
+       DNSQuestion **question2;
+       #define VALID_MSAD_SRV_TRANSPORT(T) (SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_tcp") || SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_udp"))
+       #define VALID_MSAD_SRV(Q) ((Q)->qtype == kDNSType_SRV && VALID_MSAD_SRV_TRANSPORT(SecondLabel(&(Q)->qname)))
+
+       question2 = mDNSNULL;
+       if (request->hdr.op == query_request)
+               question2 = &request->u.queryrecord.q2;
+       else if (request->hdr.op == addrinfo_request)
+               {
+               if (q->qtype == kDNSType_A)
+                       question2 = &request->u.addrinfo.q42;
+               else if (q->qtype == kDNSType_AAAA)
+                       question2 = &request->u.addrinfo.q62;
+               }
+       if (!question2)
+               {
+               LogMsg("SendAdditionalQuery: question2 NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+               return mStatus_BadParamErr;
+               }
+
+       // Sanity check: If we already sent an additonal query, we don't need to send one more.
+       //
+       // 1. When the application calls DNSServiceQueryRecord or DNSServiceGetAddrInfo with a .local name, this function
+       // is called to see whether a unicast query should be sent or not.
+       //
+       // 2. As a result of appending search domains, the question may be end up with a .local suffix even though it
+       // was not a .local name to start with. In that case, queryrecord_result_callback calls this function to
+       // send the additional query.
+       //
+       // Thus, it should not be called more than once.
+       if (*question2)
+               {
+               LogInfo("SendAdditionalQuery: question2 already sent for %##s (%s), no more q2", q->qname.c, DNSTypeName(q->qtype));
+               return err;
+               }
+
+       if (!q->ForceMCast && SameDomainLabel(LastLabel(&q->qname), (const mDNSu8 *)&localdomain))
+               if (q->qtype == kDNSType_A || q->qtype == kDNSType_AAAA || VALID_MSAD_SRV(q))
+                       {
+                       DNSQuestion *q2;
+                       int labels = CountLabels(&q->qname);
+                       q2 = mallocL("DNSQuestion", sizeof(DNSQuestion));
+                       if (!q2) FatalError("ERROR: SendAdditionalQuery malloc");
+                       *question2        = q2;
+                       *q2               = *q;
+                       q2->InterfaceID   = mDNSInterface_Unicast;
+                       q2->ExpectUnique  = mDNStrue;
+                       // If the query starts as a single label e.g., somehost, and we have search domains with .local,
+                       // queryrecord_result_callback calls this function when .local is appended to "somehost".
+                       // At that time, the name in "q" is pointing at somehost.local and its qnameOrig pointing at
+                       // "somehost". We need to copy that information so that when we retry with a different search
+                       // domain e.g., mycompany.local, we get "somehost.mycompany.local".
+                       if (q->qnameOrig)
+                               {
+                               (*question2)->qnameOrig =  mallocL("SendAdditionalQuery", DomainNameLength(q->qnameOrig));
+                               if (!(*question2)->qnameOrig) { LogMsg("SendAdditionalQuery: ERROR!!  malloc failure"); return mStatus_NoMemoryErr; }
+                               (*question2)->qnameOrig->c[0] = 0;
+                               AssignDomainName((*question2)->qnameOrig, q->qnameOrig);
+                               LogInfo("SendAdditionalQuery: qnameOrig %##s", (*question2)->qnameOrig->c);
+                               }
+                       // For names of the form "<one-or-more-labels>.bar.local." we always do a second unicast query in parallel.
+                       // For names of the form "<one-label>.local." it's less clear whether we should do a unicast query.
+                       // If the name being queried is exactly the same as the name in the DHCP "domain" option (e.g. the DHCP
+                       // "domain" is my-small-company.local, and the user types "my-small-company.local" into their web browser)
+                       // then that's a hint that it's worth doing a unicast query. Otherwise, we first check to see if the
+                       // site's DNS server claims there's an SOA record for "local", and if so, that's also a hint that queries
+                       // for names in the "local" domain will be safely answered privately before they hit the root name servers.
+                       // Note that in the "my-small-company.local" example above there will typically be an SOA record for
+                       // "my-small-company.local" but *not* for "local", which is why the "local SOA" check would fail in that case.
+                       // We need to check against both ActiveDirectoryPrimaryDomain and SearchList. If it matches against either
+                       // of those, we don't want do the SOA check for the local
+                       if (labels == 2 && !SameDomainName(&q->qname, &ActiveDirectoryPrimaryDomain) && !DomainInSearchList(&q->qname))
+                               {
+                               AssignDomainName(&q2->qname, &localdomain);
+                               q2->qtype          = kDNSType_SOA;
+                               q2->LongLived      = mDNSfalse;
+                               q2->ForceMCast     = mDNSfalse;
+                               q2->ReturnIntermed = mDNStrue;
+                               // Don't append search domains for the .local SOA query
+                               q2->AppendSearchDomains = 0;
+                               q2->AppendLocalSearchDomains = 0;
+                               q2->RetryWithSearchDomains = mDNSfalse;
+                               q2->SearchListIndex = 0;
+                               q2->TimeoutQuestion = 0;
+                               }
+                       LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast", request->sd, q2->qname.c, DNSTypeName(q2->qtype));
+                       err = mDNS_StartQuery(&mDNSStorage, q2);
+                       if (err) LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q2->qname.c, DNSTypeName(q2->qtype), (int)err);
+                       }
+       return(err);
+       }
+#endif // APPLE_OSX_mDNSResponder
+
+// This function tries to append a search domain if valid and possible. If so, returns true.
+mDNSlocal mDNSBool RetryQuestionWithSearchDomains(mDNS *const m, DNSQuestion *question, request_state *req)
+       {
+       int result;
+       // RetryWithSearchDomains tells the core to call us back so that we can retry with search domains if there is no
+       // answer in the cache or /etc/hosts. In the first call back from the core, we clear RetryWithSearchDomains so
+       // that we don't get called back repeatedly. If we got an answer from the cache or /etc/hosts, we don't touch
+       // RetryWithSearchDomains which may or may not be set.
+       //
+       // If we get e.g., NXDOMAIN and the query is neither suppressed nor exhausted the domain search list and
+       // is a valid question for appending search domains, retry by appending domains
+
+       if (!question->SuppressQuery && question->SearchListIndex != -1 && question->AppendSearchDomains)
+               {
+               question->RetryWithSearchDomains = 0;
+               result = AppendNewSearchDomain(m, question);
+               // As long as the result is either zero or 1, we retry the question. If we exahaust the search
+               // domains (result is zero) we try the original query (as it was before appending the search
+               // domains) as such on the wire as a last resort if we have not tried them before. For queries
+               // with more than one label, we have already tried them before appending search domains and
+               // hence don't retry again
+               if (result != -1)
+                       {
+                       mStatus err;
+                       err = mDNS_StartQuery(m, question);
+                       if (!err)
+                               {
+                               LogOperation("%3d: RetryQuestionWithSearchDomains(%##s, %s), retrying after appending search domain", req->sd, question->qname.c, DNSTypeName(question->qtype));
+                               // If the result was zero, it meant that there are no search domains and we just retried the question
+                               // as a single label and we should not retry with search domains anymore.
+                               if (!result) question->SearchListIndex = -1;
+                               return mDNStrue;
+                               }
+                       else
+                               {
+                               LogMsg("%3d: ERROR: RetryQuestionWithSearchDomains %##s %s mDNS_StartQuery: %d, while retrying with search domains", req->sd, question->qname.c, DNSTypeName(question->qtype), (int)err);
+                               // We have already stopped the query and could not restart. Reset the appropriate pointers
+                               // so that we don't call stop again when the question terminates
+                               question->QuestionContext = mDNSNULL;
+                               }
+                       }
+               }
+       else 
+               {
+               LogInfo("%3d: RetryQuestionWithSearchDomains: Not appending search domains - SuppressQuery %d, SearchListIndex %d, AppendSearchDomains %d", req->sd, question->SuppressQuery, question->SearchListIndex, question->AppendSearchDomains);
+               }
+       return mDNSfalse;
+       }
+
 mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
        {
        char name[MAX_ESCAPED_DOMAIN_NAME];
@@ -2268,37 +2607,160 @@ mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question,
        char *data;
        size_t len;
        DNSServiceErrorType error = kDNSServiceErr_NoError;
-       (void)m; // Unused
+       DNSQuestion *q = mDNSNULL;
 
 #if APPLE_OSX_mDNSResponder
-       if (question == &req->u.queryrecord.q2 && question->qtype != req->u.queryrecord.q.qtype && !SameDomainName(&question->qname, &req->u.queryrecord.q.qname))
+       {
+       // Sanity check: QuestionContext is set to NULL after we stop the question and hence we should not
+       // get any callbacks from the core after this.
+       if (!req)
+               {
+               LogMsg("queryrecord_result_callback: ERROR!! QuestionContext NULL for %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
+               return;
+               }
+       if (req->hdr.op == query_request && question == req->u.queryrecord.q2)
+               q = &req->u.queryrecord.q;
+       else if (req->hdr.op == addrinfo_request && question == req->u.addrinfo.q42)
+               q = &req->u.addrinfo.q4;
+       else if (req->hdr.op == addrinfo_request && question == req->u.addrinfo.q62)
+               q = &req->u.addrinfo.q6;
+       
+       if (q && question->qtype != q->qtype && !SameDomainName(&question->qname, &q->qname))
                {
-               mDNS_StopQuery(&mDNSStorage, question);
-               question->QuestionCallback = mDNSNULL;
+               mStatus err;
+               domainname *orig = question->qnameOrig;
+
+               LogInfo("queryrecord_result_callback: Stopping q2 local %##s", question->qname.c);
+               mDNS_StopQuery(m, question);
+               question->QuestionContext = mDNSNULL;
+
+               // We got a negative response for the SOA record indicating that .local does not exist.
+               // But we might have other search domains (that does not end in .local) that can be
+               // appended to this question. In that case, we want to retry the question. Otherwise,
+               // we don't want to try this question as unicast.
+               if (answer->RecordType == kDNSRecordTypePacketNegative && !q->AppendSearchDomains)
+                       {
+                       LogInfo("queryrecord_result_callback: question %##s AppendSearchDomains zero", q->qname.c);
+                       return;
+                       }
+
                // If we got a non-negative answer for our "local SOA" test query, start an additional parallel unicast query
-               if (answer->RecordType != kDNSRecordTypePacketNegative)
+               //
+               // Note: When we copy the original question, we copy everything including the AppendSearchDomains,
+               // RetryWithSearchDomains except for qnameOrig which can be non-NULL if the original question is
+               // e.g., somehost and then we appended e.g., ".local" and retried that question. See comment in
+               // SendAdditionalQuery as to how qnameOrig gets initialized.
+               *question              = *q;
+               question->InterfaceID  = mDNSInterface_Unicast;
+               question->ExpectUnique = mDNStrue;
+               question->qnameOrig    = orig;
+
+               LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast, context %p", req->sd, question->qname.c, DNSTypeName(question->qtype), question->QuestionContext);
+
+               // If the original question timed out, its QuestionContext would already be set to NULL and that's what we copied above.
+               // Hence, we need to set it explicitly here.
+               question->QuestionContext = req;
+               err = mDNS_StartQuery(m, question);
+               if (err) LogMsg("%3d: ERROR: queryrecord_result_callback %##s %s mDNS_StartQuery: %d", req->sd, question->qname.c, DNSTypeName(question->qtype), (int)err);
+
+               // If we got a positive response to local SOA, then try the .local question as unicast
+               if (answer->RecordType != kDNSRecordTypePacketNegative) return;
+
+               // Fall through and get the next search domain. The question is pointing at .local
+               // and we don't want to try that. Try the next search domain. Don't try with local
+               // search domains for the unicast question anymore.
+               //
+               // Note: we started the question above which will be stopped immediately (never sent on the wire)
+               // before we pick the next search domain below. RetryQuestionWithSearchDomains assumes that the
+               // question has already started.
+               question->AppendLocalSearchDomains = 0;
+               }
+
+       if (q && AddRecord && (question->InterfaceID == mDNSInterface_Unicast) && !answer->rdlength)
+               {
+               // If we get a negative response to the unicast query that we sent above, retry after appending search domains
+               // Note: We could have appended search domains below (where do it for regular unicast questions) instead of doing it here. 
+               // As we ignore negative unicast answers below, we would never reach the code where the search domains are appended.
+               // To keep things simple, we handle unicast ".local" separately here.
+               LogInfo("queryrecord_result_callback: Retrying .local question %##s (%s) as unicast after appending search domains", question->qname.c, DNSTypeName(question->qtype));
+               if (RetryQuestionWithSearchDomains(m, question, req))
+                       return;
+               if (question->AppendSearchDomains && !question->AppendLocalSearchDomains && IsLocalDomain(&question->qname))
                        {
-                       *question              = req->u.queryrecord.q;
-                       question->InterfaceID  = mDNSInterface_Unicast;
-                       question->ExpectUnique = mDNStrue;
-                       LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast", req->sd, question->qname.c, DNSTypeName(question->qtype));
-                       mStatus err = mDNS_StartQuery(&mDNSStorage, question);
-                       if (err) LogMsg("%3d: ERROR: queryrecord_result_callback %##s %s mDNS_StartQuery: %d", req->sd, question->qname.c, DNSTypeName(question->qtype), (int)err);
+                       // If "local" is the last search domain, we need to stop the question so that we don't send the "local"
+                       // question on the wire as we got a negative response for the local SOA. But, we can't stop the question
+                       // yet as we may have to timeout the question (done by the "core") for which we need to leave the question
+                       // in the list. We leave it disabled so that it does not hit the wire.
+                       LogInfo("queryrecord_result_callback: Disabling .local question %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
+                       question->ThisQInterval = 0;
                        }
-               return;
                }
+       // If we are here it means that either "question" is not "q2" OR we got a positive response for "q2" OR we have no more search
+       // domains to append for "q2". In all cases, fall through and deliver the response
+       }
 #endif // APPLE_OSX_mDNSResponder
 
        if (answer->RecordType == kDNSRecordTypePacketNegative)
                {
+               // If this question needs to be timed out and we have reached the stop time, mark
+               // the error as timeout. It is possible that we might get a negative response from an
+               // external DNS server at the same time when this question reaches its stop time. We
+               // can't tell the difference as there is no indication in the callback. This should
+               // be okay as we will be timing out this query anyway.
+               mDNS_Lock(m);
+               if (question->TimeoutQuestion)
+                       {
+                       if ((m->timenow - question->StopTime) >= 0)
+                               {
+                               LogInfo("queryrecord_result_callback:Question %##s (%s) timing out, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID);
+                               error = kDNSServiceErr_Timeout;
+                               }
+                       }
+               mDNS_Unlock(m);
                // When we're doing parallel unicast and multicast queries for dot-local names (for supporting Microsoft
                // Active Directory sites) we need to ignore negative unicast answers. Otherwise we'll generate negative
                // answers for just about every single multicast name we ever look up, since the Microsoft Active Directory
                // server is going to assert that pretty much every single multicast name doesn't exist.
-               if (!answer->InterfaceID && IsLocalDomain(answer->name)) return;
-               error = kDNSServiceErr_NoSuchRecord;
+               //
+               // If we are timing out this query, we need to deliver the negative answer to the application
+               if (error != kDNSServiceErr_Timeout)
+                       {
+                       if (!answer->InterfaceID && IsLocalDomain(answer->name))
+                               {
+                               LogInfo("queryrecord_result_callback:Question %##s (%s) answering local with unicast", question->qname.c, DNSTypeName(question->qtype));
+                               return;
+                               }
+                       error = kDNSServiceErr_NoSuchRecord;
+                       }
                AddRecord = mDNStrue;
                }
+       // If we get a negative answer, try appending search domains. Don't append search domains
+       // - if we are timing out this question
+       // - if the negative response was received as a result of a multicast query
+       // - if this is an additional query (q2), we already appended search domains above (indicated by "!q" below)
+       if (error != kDNSServiceErr_Timeout)
+               {
+               if (!q && !answer->InterfaceID && !answer->rdlength && AddRecord)
+                       {
+                       // If the original question did not end in .local, we did not send an SOA query
+                       // to figure out whether we should send an additional unicast query or not. If we just
+                       // appended .local, we need to see if we need to send an additional query. This should
+                       // normally happen just once because after we append .local, we ignore all negative
+                       // responses for .local above.
+                       LogInfo("queryrecord_result_callback: Retrying question %##s (%s) after appending search domains", question->qname.c, DNSTypeName(question->qtype));
+                       if (RetryQuestionWithSearchDomains(m, question, req))
+                               {
+                               // Note: We need to call SendAdditionalQuery every time after appending a search domain as .local could
+                               // be anywhere in the search domain list.
+#if APPLE_OSX_mDNSResponder
+                               mStatus err = mStatus_NoError;
+                               err = SendAdditionalQuery(question, req, err);
+                               if (err) LogMsg("queryrecord_result_callback: Sending .local SOA query failed, after appending domains");
+#endif // APPLE_OSX_mDNSResponder
+                               return;
+                               }
+                       }
+               }
 
        ConvertDomainNameToCString(answer->name, name);
 
@@ -2317,7 +2779,14 @@ mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question,
        rep = create_reply(req->hdr.op == query_request ? query_reply_op : addrinfo_reply_op, len, req);
 
        rep->rhdr->flags = dnssd_htonl(AddRecord ? kDNSServiceFlagsAdd : 0);
-       rep->rhdr->ifi   = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID));
+       // Call mDNSPlatformInterfaceIndexfromInterfaceID, but suppressNetworkChange (last argument). Otherwise, if the
+       // InterfaceID is not valid, then it simulates a "NetworkChanged" which in turn makes questions
+       // to be stopped and started including  *this* one. Normally the InterfaceID is valid. But when we
+       // are using the /etc/hosts entries to answer a question, the InterfaceID may not be known to the
+       // mDNS core . Eventually, we should remove the calls to "NetworkChanged" in
+       // mDNSPlatformInterfaceIndexfromInterfaceID when it can't find InterfaceID as ResourceRecords
+       // should not have existed to answer this question if the corresponding interface is not valid.
+       rep->rhdr->ifi   = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNStrue));
        rep->rhdr->error = dnssd_htonl(error);
 
        data = (char *)&rep->rhdr[1];
@@ -2335,6 +2804,13 @@ mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question,
        put_uint32(AddRecord ? answer->rroriginalttl : 0, &data);
 
        append_reply(req, rep);
+       // Stop the question, if we just timed out
+       if (error == kDNSServiceErr_Timeout)
+               {
+               mDNS_StopQuery(m, question);
+               // Reset the pointers so that we don't call stop on termination
+               question->QuestionContext = mDNSNULL;
+               }
 #if APPLE_OSX_mDNSResponder
 #if ! NO_WCF
        CHECK_WCF_FUNCTION(WCFIsServerRunning)
@@ -2412,10 +2888,48 @@ mDNSlocal void queryrecord_termination_callback(request_state *request)
        {
        LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) STOP",
                request->sd, request->u.queryrecord.q.qname.c, DNSTypeName(request->u.queryrecord.q.qtype));
-       mDNS_StopQuery(&mDNSStorage, &request->u.queryrecord.q);  // no need to error check
-       if (request->u.queryrecord.q.InterfaceID == mDNSInterface_P2P || (!request->u.queryrecord.q.InterfaceID && SameDomainName((const domainname *)LastLabel(&request->u.queryrecord.q.qname), &localdomain)))
+       if (request->u.queryrecord.q.QuestionContext)
+               {
+               mDNS_StopQuery(&mDNSStorage, &request->u.queryrecord.q);  // no need to error check
+               request->u.queryrecord.q.QuestionContext = mDNSNULL;
+               }
+       else
+               {
+               DNSQuestion *question = &request->u.queryrecord.q;
+               LogInfo("queryrecord_termination_callback: question %##s (%s) already stopped, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID);
+               }
+
+       if (request->u.queryrecord.q.qnameOrig)
+               {
+               freeL("QueryTermination", request->u.queryrecord.q.qnameOrig);
+               request->u.queryrecord.q.qnameOrig = mDNSNULL;
+               }
+       if (request->u.queryrecord.q.InterfaceID == mDNSInterface_P2P || (!request->u.queryrecord.q.InterfaceID && SameDomainName((const domainname *)LastLabel(&request->u.queryrecord.q.qname), &localdomain) && (request->flags & kDNSServiceFlagsIncludeP2P)))
+               {
+               LogInfo("queryrecord_termination_callback: calling external_stop_browsing_for_service()");
                external_stop_browsing_for_service(&mDNSStorage, &request->u.queryrecord.q.qname, request->u.queryrecord.q.qtype);
-       if (request->u.queryrecord.q2.QuestionCallback) mDNS_StopQuery(&mDNSStorage, &request->u.queryrecord.q2);
+               }
+       if (request->u.queryrecord.q2)
+               {
+               if (request->u.queryrecord.q2->QuestionContext)
+                       {
+                       LogInfo("queryrecord_termination_callback: Stopping q2 %##s", request->u.queryrecord.q2->qname.c);
+                       mDNS_StopQuery(&mDNSStorage, request->u.queryrecord.q2);
+                       }
+               else 
+                       {
+                       DNSQuestion *question = request->u.queryrecord.q2;
+                       LogInfo("queryrecord_termination_callback: q2 %##s (%s) already stopped, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID);
+                       }
+               if (request->u.queryrecord.q2->qnameOrig)
+                       {
+                       LogInfo("queryrecord_termination_callback: freeing q2 qnameOrig %##s", request->u.queryrecord.q2->qnameOrig->c);
+                       freeL("QueryTermination q2", request->u.queryrecord.q2->qnameOrig);
+                       request->u.queryrecord.q2->qnameOrig = mDNSNULL;
+                       }
+               freeL("queryrecord Q2", request->u.queryrecord.q2);
+               request->u.queryrecord.q2 = mDNSNULL;
+               }
        }
 
 mDNSlocal mStatus handle_queryrecord_request(request_state *request)
@@ -2437,6 +2951,7 @@ mDNSlocal mStatus handle_queryrecord_request(request_state *request)
        if (!request->msgptr)
                { LogMsg("%3d: DNSServiceQueryRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
 
+       request->flags = flags;
        mDNSPlatformMemZero(&request->u.queryrecord, sizeof(request->u.queryrecord));
 
        q->InterfaceID      = InterfaceID;
@@ -2451,10 +2966,40 @@ mDNSlocal mStatus handle_queryrecord_request(request_state *request)
        q->ExpectUnique     = mDNSfalse;
        q->ForceMCast       = (flags & kDNSServiceFlagsForceMulticast     ) != 0;
        q->ReturnIntermed   = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
-       q->SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable) != 0;
-       q->WakeOnResolve    = mDNSfalse;
+       q->SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable   ) != 0;
+       q->TimeoutQuestion  = (flags & kDNSServiceFlagsTimeout            ) != 0;
+       q->WakeOnResolve    = 0;
        q->QuestionCallback = queryrecord_result_callback;
        q->QuestionContext  = request;
+       q->SearchListIndex  = 0;
+
+       // Don't append search domains for fully qualified domain names including queries
+       // such as e.g., "abc." that has only one label. We convert all names to FQDNs as internally
+       // we only deal with FQDNs. Hence, we cannot look at qname to figure out whether we should
+       // append search domains or not.  So, we record that information in AppendSearchDomains.
+       //
+       // We append search domains only for queries that are a single label. If overriden using
+       // command line argument "AlwaysAppendSearchDomains", then we do it for any query which
+       // is not fully qualified.
+
+       if ((rrtype == kDNSType_A || rrtype == kDNSType_AAAA) && name[strlen(name) - 1] != '.' &&
+               (AlwaysAppendSearchDomains || CountLabels(&q->qname) == 1))
+               {
+               q->AppendSearchDomains = 1;
+               q->AppendLocalSearchDomains = 1;
+               }
+       else
+               {
+               q->AppendSearchDomains = 0;
+               q->AppendLocalSearchDomains = 0;
+               }
+
+       // For single label queries that are not fully qualified, look at /etc/hosts, cache and try
+       // search domains before trying them on the wire as a single label query. RetryWithSearchDomains
+       // tell the core to call back into the UDS layer if there is no valid response in /etc/hosts or
+       // the cache
+       q->RetryWithSearchDomains = ApplySearchDomainsFirst(q) ? 1 : 0;
+       q->qnameOrig        = mDNSNULL;
 
        LogOperation("%3d: DNSServiceQueryRecord(%X, %d, %##s, %s) START", request->sd, flags, interfaceIndex, q->qname.c, DNSTypeName(q->qtype));
        err = mDNS_StartQuery(&mDNSStorage, q);
@@ -2462,46 +3007,15 @@ mDNSlocal mStatus handle_queryrecord_request(request_state *request)
        else 
                {
                request->terminate = queryrecord_termination_callback;
-               if (q->InterfaceID == mDNSInterface_P2P || (!q->InterfaceID && SameDomainName((const domainname *)LastLabel(&q->qname), &localdomain)))
+               if (q->InterfaceID == mDNSInterface_P2P || (!q->InterfaceID && SameDomainName((const domainname *)LastLabel(&q->qname), &localdomain) && (flags & kDNSServiceFlagsIncludeP2P)))
+                       {
+                       LogInfo("handle_queryrecord_request: calling external_start_browsing_for_service()");
                        external_start_browsing_for_service(&mDNSStorage, &q->qname, q->qtype);
+                       }
                }
 
 #if APPLE_OSX_mDNSResponder
-       // Workaround for networks using Microsoft Active Directory using "local" as a private internal top-level domain
-       extern domainname ActiveDirectoryPrimaryDomain;
-       #define VALID_MSAD_SRV_TRANSPORT(T) (SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_tcp") || SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_udp"))
-       #define VALID_MSAD_SRV(Q) ((Q)->qtype == kDNSType_SRV && VALID_MSAD_SRV_TRANSPORT(SecondLabel(&(Q)->qname)))
-
-       if (!q->ForceMCast && SameDomainLabel(LastLabel(&q->qname), (const mDNSu8 *)&localdomain))
-               if (q->qtype == kDNSType_A || q->qtype == kDNSType_AAAA || VALID_MSAD_SRV(q))
-                       {
-                       int labels = CountLabels(&q->qname);
-                       DNSQuestion *const q2 = &request->u.queryrecord.q2;
-                       *q2              = *q;
-                       q2->InterfaceID  = mDNSInterface_Unicast;
-                       q2->ExpectUnique = mDNStrue;
-       
-                       // For names of the form "<one-or-more-labels>.bar.local." we always do a second unicast query in parallel.
-                       // For names of the form "<one-label>.local." it's less clear whether we should do a unicast query.
-                       // If the name being queried is exactly the same as the name in the DHCP "domain" option (e.g. the DHCP
-                       // "domain" is my-small-company.local, and the user types "my-small-company.local" into their web browser)
-                       // then that's a hint that it's worth doing a unicast query. Otherwise, we first check to see if the
-                       // site's DNS server claims there's an SOA record for "local", and if so, that's also a hint that queries
-                       // for names in the "local" domain will be safely answered privately before they hit the root name servers.
-                       // Note that in the "my-small-company.local" example above there will typically be an SOA record for
-                       // "my-small-company.local" but *not* for "local", which is why the "local SOA" check would fail in that case.
-                       if (labels == 2 && !SameDomainName(&q->qname, &ActiveDirectoryPrimaryDomain))
-                               {
-                               AssignDomainName(&q2->qname, &localdomain);
-                               q2->qtype          = kDNSType_SOA;
-                               q2->LongLived      = mDNSfalse;
-                               q2->ForceMCast     = mDNSfalse;
-                               q2->ReturnIntermed = mDNStrue;
-                               }
-                       LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast", request->sd, q2->qname.c, DNSTypeName(q2->qtype));
-                       err = mDNS_StartQuery(&mDNSStorage, q2);
-                       if (err) LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q2->qname.c, DNSTypeName(q2->qtype), (int)err);
-                       }
+       err = SendAdditionalQuery(q, request, err);
 #endif // APPLE_OSX_mDNSResponder
 
        return(err);
@@ -2640,7 +3154,7 @@ mDNSlocal mStatus handle_reconfirm_request(request_state *request)
                        "%3d: DNSServiceReconfirmRecord(%s) interface %d initiated" :
                        "%3d: DNSServiceReconfirmRecord(%s) interface %d failed: %d",
                        request->sd, RRDisplayString(&mDNSStorage, &rr->resrec),
-                       mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, rr->resrec.InterfaceID), status);
+                       mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, rr->resrec.InterfaceID, mDNSfalse), status);
                freeL("AuthRecord/handle_reconfirm_request", rr);
                }
        return(status);
@@ -2722,7 +3236,7 @@ mDNSlocal void port_mapping_create_request_callback(mDNS *m, NATTraversalInfo *n
        rep = create_reply(port_mapping_reply_op, replyLen, request);
 
        rep->rhdr->flags = dnssd_htonl(0);
-       rep->rhdr->ifi   = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, n->InterfaceID));
+       rep->rhdr->ifi   = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, n->InterfaceID, mDNSfalse));
        rep->rhdr->error = dnssd_htonl(n->Result);
 
        data = (char *)&rep->rhdr[1];
@@ -2811,12 +3325,54 @@ mDNSlocal void addrinfo_termination_callback(request_state *request)
                mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q4);
                request->u.addrinfo.q4.QuestionContext = mDNSNULL;
                }
+       if (request->u.addrinfo.q4.qnameOrig)
+               {
+               freeL("QueryTermination", request->u.addrinfo.q4.qnameOrig);
+               request->u.addrinfo.q4.qnameOrig = mDNSNULL;
+               }
+       if (request->u.addrinfo.q42)
+               {
+               if (request->u.addrinfo.q42->QuestionContext)
+                       {
+                       LogInfo("addrinfo_termination_callback: Stopping q42 %##s", request->u.addrinfo.q42->qname.c);
+                       mDNS_StopQuery(&mDNSStorage, request->u.addrinfo.q42);
+                       }
+               if (request->u.addrinfo.q42->qnameOrig)
+                       {
+                       LogInfo("addrinfo_termination_callback: freeing q42 qnameOrig %##s", request->u.addrinfo.q42->qnameOrig->c);
+                       freeL("QueryTermination q42", request->u.addrinfo.q42->qnameOrig);
+                       request->u.addrinfo.q42->qnameOrig = mDNSNULL;
+                       }
+               freeL("addrinfo Q42", request->u.addrinfo.q42);
+               request->u.addrinfo.q42 = mDNSNULL;
+               }
 
        if (request->u.addrinfo.q6.QuestionContext)
                {
                mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q6);
                request->u.addrinfo.q6.QuestionContext = mDNSNULL;
                }
+       if (request->u.addrinfo.q6.qnameOrig)
+               {
+               freeL("QueryTermination", request->u.addrinfo.q6.qnameOrig);
+               request->u.addrinfo.q6.qnameOrig = mDNSNULL;
+               }
+       if (request->u.addrinfo.q62)
+               {
+               if (request->u.addrinfo.q62->QuestionContext)
+                       {
+                       LogInfo("addrinfo_termination_callback: Stopping q62 %##s", request->u.addrinfo.q62->qname.c);
+                       mDNS_StopQuery(&mDNSStorage, request->u.addrinfo.q62);
+                       }
+               if (request->u.addrinfo.q62->qnameOrig)
+                       {
+                       LogInfo("addrinfo_termination_callback: freeing q62 qnameOrig %##s", request->u.addrinfo.q62->qnameOrig->c);
+                       freeL("QueryTermination q62", request->u.addrinfo.q62->qnameOrig);
+                       request->u.addrinfo.q62->qnameOrig = mDNSNULL;
+                       }
+               freeL("addrinfo Q62", request->u.addrinfo.q62);
+               request->u.addrinfo.q62 = mDNSNULL;
+               }
        }
 
 mDNSlocal mStatus handle_addrinfo_request(request_state *request)
@@ -2862,11 +3418,29 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request)
        request->u.addrinfo.q4.ForceMCast       = request->u.addrinfo.q6.ForceMCast       = (flags & kDNSServiceFlagsForceMulticast     ) != 0;
        request->u.addrinfo.q4.ReturnIntermed   = request->u.addrinfo.q6.ReturnIntermed   = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
        request->u.addrinfo.q4.SuppressUnusable = request->u.addrinfo.q6.SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable   ) != 0;
-       request->u.addrinfo.q4.SuppressUnusable = request->u.addrinfo.q6.SuppressUnusable = mDNSfalse;
+       request->u.addrinfo.q4.TimeoutQuestion  = request->u.addrinfo.q6.TimeoutQuestion  = (flags & kDNSServiceFlagsTimeout            ) != 0;
+       request->u.addrinfo.q4.WakeOnResolve    = request->u.addrinfo.q6.WakeOnResolve    = 0;
+       request->u.addrinfo.q4.qnameOrig        = request->u.addrinfo.q6.qnameOrig        = mDNSNULL;
 
        if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4)
                {
                request->u.addrinfo.q4.qtype            = kDNSServiceType_A;
+               request->u.addrinfo.q4.SearchListIndex  = 0;
+
+               // We append search domains only for queries that are a single label. If overriden using
+               // command line argument "AlwaysAppendSearchDomains", then we do it for any query which
+               // is not fully qualified.
+               if (hostname[strlen(hostname) - 1] != '.' && (AlwaysAppendSearchDomains || CountLabels(&d) == 1))
+                       {
+                       request->u.addrinfo.q4.AppendSearchDomains = 1;
+                       request->u.addrinfo.q4.AppendLocalSearchDomains = 1;
+                       }
+               else
+                       {
+                       request->u.addrinfo.q4.AppendSearchDomains = 0;
+                       request->u.addrinfo.q4.AppendLocalSearchDomains = 0;
+                       }
+               request->u.addrinfo.q4.RetryWithSearchDomains = (ApplySearchDomainsFirst(&request->u.addrinfo.q4) ? 1 : 0);
                request->u.addrinfo.q4.QuestionCallback = queryrecord_result_callback;
                request->u.addrinfo.q4.QuestionContext  = request;
                err = mDNS_StartQuery(&mDNSStorage, &request->u.addrinfo.q4);
@@ -2875,11 +3449,26 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request)
                        LogMsg("ERROR: mDNS_StartQuery: %d", (int)err);
                        request->u.addrinfo.q4.QuestionContext = mDNSNULL;
                        }
+               #if APPLE_OSX_mDNSResponder
+               err = SendAdditionalQuery(&request->u.addrinfo.q4, request, err);
+               #endif // APPLE_OSX_mDNSResponder
                }
 
        if (!err && (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6))
                {
                request->u.addrinfo.q6.qtype            = kDNSServiceType_AAAA;
+               request->u.addrinfo.q6.SearchListIndex  = 0;
+               if (hostname[strlen(hostname) - 1] != '.' && (AlwaysAppendSearchDomains || CountLabels(&d) == 1))
+                       {
+                       request->u.addrinfo.q6.AppendSearchDomains = 1;
+                       request->u.addrinfo.q6.AppendLocalSearchDomains = 1;
+                       }
+               else
+                       {
+                       request->u.addrinfo.q6.AppendSearchDomains = 0;
+                       request->u.addrinfo.q6.AppendLocalSearchDomains = 0;
+                       }
+               request->u.addrinfo.q6.RetryWithSearchDomains = (ApplySearchDomainsFirst(&request->u.addrinfo.q6) ? 1 : 0);
                request->u.addrinfo.q6.QuestionCallback = queryrecord_result_callback;
                request->u.addrinfo.q6.QuestionContext  = request;
                err = mDNS_StartQuery(&mDNSStorage, &request->u.addrinfo.q6);
@@ -2894,6 +3483,9 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request)
                                request->u.addrinfo.q4.QuestionContext = mDNSNULL;
                                }
                        }
+               #if APPLE_OSX_mDNSResponder
+               err = SendAdditionalQuery(&request->u.addrinfo.q6, request, err);
+               #endif // APPLE_OSX_mDNSResponder
                }
 
        LogOperation("%3d: DNSServiceGetAddrInfo(%X, %d, %d, %##s) START",
@@ -3007,7 +3599,7 @@ mDNSlocal void read_msg(request_state *req)
                LogMsg("%3d: Got       %d %d %d %d", req->sd, msg.msg_controllen, cmsg->cmsg_len, cmsg->cmsg_level, cmsg->cmsg_type);
 #endif // DEBUG_64BIT_SCM_RIGHTS
                if (msg.msg_controllen == sizeof(cbuf) &&
-                       cmsg->cmsg_len     == sizeof(cbuf) &&
+                       cmsg->cmsg_len     == CMSG_LEN(sizeof(dnssd_sock_t)) &&
                        cmsg->cmsg_level   == SOL_SOCKET   &&
                        cmsg->cmsg_type    == SCM_RIGHTS)
                        {
@@ -3130,124 +3722,127 @@ mDNSlocal void request_callback(int fd, short filter, void *info)
        (void)fd; // Unused
        (void)filter; // Unused
 
-       read_msg(req);
-       if (req->ts == t_morecoming) return;
-       if (req->ts == t_terminated || req->ts == t_error) { AbortUnlinkAndFree(req); return; }
-       if (req->ts != t_complete) { LogMsg("req->ts %d != t_complete", req->ts); AbortUnlinkAndFree(req); return; }
-
-       if (req->hdr.version != VERSION)
+       for (;;)
                {
-               LogMsg("ERROR: client version %d incompatible with daemon version %d", req->hdr.version, VERSION);
-               AbortUnlinkAndFree(req);
-               return;
-               }
+               read_msg(req);
+               if (req->ts == t_morecoming) return;
+               if (req->ts == t_terminated || req->ts == t_error) { AbortUnlinkAndFree(req); return; }
+               if (req->ts != t_complete) { LogMsg("req->ts %d != t_complete", req->ts); AbortUnlinkAndFree(req); return; }
+
+               if (req->hdr.version != VERSION)
+                       {
+                       LogMsg("ERROR: client version %d incompatible with daemon version %d", req->hdr.version, VERSION);
+                       AbortUnlinkAndFree(req);
+                       return;
+                       }
+
+               switch(req->hdr.op)            //          Interface       + other data
+                       {
+                       case connection_request:       min_size = 0;                                                                           break;
+                       case reg_service_request:      min_size += sizeof(mDNSu32) + 4 /* name, type, domain, host */ + 4 /* port, textlen */; break;
+                       case add_record_request:       min_size +=                   4 /* type, rdlen */              + 4 /* ttl */;           break;
+                       case update_record_request:    min_size +=                   2 /* rdlen */                    + 4 /* ttl */;           break;
+                       case remove_record_request:                                                                                            break;
+                       case browse_request:           min_size += sizeof(mDNSu32) + 2 /* type, domain */;                                     break;
+                       case resolve_request:          min_size += sizeof(mDNSu32) + 3 /* type, type, domain */;                               break;
+                       case query_request:            min_size += sizeof(mDNSu32) + 1 /* name */                     + 4 /* type, class*/;    break;
+                       case enumeration_request:      min_size += sizeof(mDNSu32);                                                            break;
+                       case reg_record_request:       min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */ + 4 /* ttl */;  break;
+                       case reconfirm_record_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */;                break;
+                       case setdomain_request:        min_size +=                   1 /* domain */;                                           break;
+                       case getproperty_request:      min_size = 2;                                                                           break;
+                       case port_mapping_request:     min_size += sizeof(mDNSu32) + 4 /* udp/tcp */ + 4 /* int/ext port */    + 4 /* ttl */;  break;
+                       case addrinfo_request:         min_size += sizeof(mDNSu32) + 4 /* v4/v6 */   + 1 /* hostname */;                       break;
+                       case send_bpf:                 // Same as cancel_request below
+                       case cancel_request:           min_size = 0;                                                                           break;
+                       default: LogMsg("ERROR: validate_message - unsupported req type: %d", req->hdr.op); min_size = -1;                     break;
+                       }
+
+               if ((mDNSs32)req->data_bytes < min_size)
+                       { LogMsg("Invalid message %d bytes; min for %d is %d", req->data_bytes, req->hdr.op, min_size); AbortUnlinkAndFree(req); return; }
+
+               if (LightweightOp(req->hdr.op) && !req->terminate)
+                       { LogMsg("Reg/Add/Update/Remove %d require existing connection", req->hdr.op);                  AbortUnlinkAndFree(req); return; }
+
+               // check if client wants silent operation
+               if (req->hdr.ipc_flags & IPC_FLAGS_NOREPLY) req->no_reply = 1;
+
+               // If req->terminate is already set, this means this operation is sharing an existing connection
+               if (req->terminate && !LightweightOp(req->hdr.op))
+                       {
+                       request_state *newreq = NewRequest();
+                       newreq->primary = req;
+                       newreq->sd      = req->sd;
+                       newreq->errsd   = req->errsd;
+                       newreq->uid     = req->uid;
+                       newreq->hdr     = req->hdr;
+                       newreq->msgbuf  = req->msgbuf;
+                       newreq->msgptr  = req->msgptr;
+                       newreq->msgend  = req->msgend;
+                       req = newreq;
+                       }
+
+               // If we're shutting down, don't allow new client requests
+               // We do allow "cancel" and "getproperty" during shutdown
+               if (mDNSStorage.ShutdownTime && req->hdr.op != cancel_request && req->hdr.op != getproperty_request)
+                       {
+                       err = mStatus_ServiceNotRunning;
+                       }
+               else switch(req->hdr.op)
+                       {
+                       // These are all operations that have their own first-class request_state object
+                       case connection_request:           LogOperation("%3d: DNSServiceCreateConnection START", req->sd);
+                                                                                          req->terminate = connection_termination; break;
+                       case resolve_request:              err = handle_resolve_request     (req);  break;
+                       case query_request:                err = handle_queryrecord_request (req);  break;
+                       case browse_request:               err = handle_browse_request      (req);  break;
+                       case reg_service_request:          err = handle_regservice_request  (req);  break;
+                       case enumeration_request:          err = handle_enum_request        (req);  break;
+                       case reconfirm_record_request:     err = handle_reconfirm_request   (req);  break;
+                       case setdomain_request:            err = handle_setdomain_request   (req);  break;
+                       case getproperty_request:                handle_getproperty_request (req);  break;
+                       case port_mapping_request:         err = handle_port_mapping_request(req);  break;
+                       case addrinfo_request:             err = handle_addrinfo_request    (req);  break;
+                       case send_bpf:                     /* Do nothing for send_bpf */            break;
+
+                       // These are all operations that work with an existing request_state object
+                       case reg_record_request:           err = handle_regrecord_request   (req);  break;
+                       case add_record_request:           err = handle_add_request         (req);  break;
+                       case update_record_request:        err = handle_update_request      (req);  break;
+                       case remove_record_request:        err = handle_removerecord_request(req);  break;
+                       case cancel_request:                     handle_cancel_request      (req);  break;
+                       default: LogMsg("%3d: ERROR: Unsupported UDS req: %d", req->sd, req->hdr.op);
+                       }
 
-       switch(req->hdr.op)            //          Interface       + other data
-               {
-               case connection_request:       min_size = 0;                                                                           break;
-               case reg_service_request:      min_size += sizeof(mDNSu32) + 4 /* name, type, domain, host */ + 4 /* port, textlen */; break;
-               case add_record_request:       min_size +=                   4 /* type, rdlen */              + 4 /* ttl */;           break;
-               case update_record_request:    min_size +=                   2 /* rdlen */                    + 4 /* ttl */;           break;
-               case remove_record_request:                                                                                            break;
-               case browse_request:           min_size += sizeof(mDNSu32) + 2 /* type, domain */;                                     break;
-               case resolve_request:          min_size += sizeof(mDNSu32) + 3 /* type, type, domain */;                               break;
-               case query_request:            min_size += sizeof(mDNSu32) + 1 /* name */                     + 4 /* type, class*/;    break;
-               case enumeration_request:      min_size += sizeof(mDNSu32);                                                            break;
-               case reg_record_request:       min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */ + 4 /* ttl */;  break;
-               case reconfirm_record_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */;                break;
-               case setdomain_request:        min_size +=                   1 /* domain */;                                           break;
-               case getproperty_request:      min_size = 2;                                                                           break;
-               case port_mapping_request:     min_size += sizeof(mDNSu32) + 4 /* udp/tcp */ + 4 /* int/ext port */    + 4 /* ttl */;  break;
-               case addrinfo_request:         min_size += sizeof(mDNSu32) + 4 /* v4/v6 */   + 1 /* hostname */;                       break;
-               case send_bpf:                 // Same as cancel_request below
-               case cancel_request:           min_size = 0;                                                                           break;
-               default: LogMsg("ERROR: validate_message - unsupported req type: %d", req->hdr.op); min_size = -1;                     break;
-               }
-
-       if ((mDNSs32)req->data_bytes < min_size)
-               { LogMsg("Invalid message %d bytes; min for %d is %d", req->data_bytes, req->hdr.op, min_size); AbortUnlinkAndFree(req); return; }
-
-       if (LightweightOp(req->hdr.op) && !req->terminate)
-               { LogMsg("Reg/Add/Update/Remove %d require existing connection", req->hdr.op);                  AbortUnlinkAndFree(req); return; }
-
-       // check if client wants silent operation
-       if (req->hdr.ipc_flags & IPC_FLAGS_NOREPLY) req->no_reply = 1;
-
-       // If req->terminate is already set, this means this operation is sharing an existing connection
-       if (req->terminate && !LightweightOp(req->hdr.op))
-               {
-               request_state *newreq = NewRequest();
-               newreq->primary = req;
-               newreq->sd      = req->sd;
-               newreq->errsd   = req->errsd;
-               newreq->uid     = req->uid;
-               newreq->hdr     = req->hdr;
-               newreq->msgbuf  = req->msgbuf;
-               newreq->msgptr  = req->msgptr;
-               newreq->msgend  = req->msgend;
-               req = newreq;
-               }
-
-       // If we're shutting down, don't allow new client requests
-       // We do allow "cancel" and "getproperty" during shutdown
-       if (mDNSStorage.ShutdownTime && req->hdr.op != cancel_request && req->hdr.op != getproperty_request)
-               {
-               err = mStatus_ServiceNotRunning;
-               }
-       else switch(req->hdr.op)
-               {
-               // These are all operations that have their own first-class request_state object
-               case connection_request:           LogOperation("%3d: DNSServiceCreateConnection START", req->sd);
-                                                      req->terminate = connection_termination; break;
-               case resolve_request:              err = handle_resolve_request     (req);  break;
-               case query_request:                err = handle_queryrecord_request (req);  break;
-               case browse_request:               err = handle_browse_request      (req);  break;
-               case reg_service_request:          err = handle_regservice_request  (req);  break;
-               case enumeration_request:          err = handle_enum_request        (req);  break;
-               case reconfirm_record_request:     err = handle_reconfirm_request   (req);  break;
-               case setdomain_request:            err = handle_setdomain_request   (req);  break;
-               case getproperty_request:                handle_getproperty_request (req);  break;
-               case port_mapping_request:         err = handle_port_mapping_request(req);  break;
-               case addrinfo_request:             err = handle_addrinfo_request    (req);  break;
-               case send_bpf:                     /* Do nothing for send_bpf */            break;
-
-               // These are all operations that work with an existing request_state object
-               case reg_record_request:           err = handle_regrecord_request   (req);  break;
-               case add_record_request:           err = handle_add_request         (req);  break;
-               case update_record_request:        err = handle_update_request      (req);  break;
-               case remove_record_request:        err = handle_removerecord_request(req);  break;
-               case cancel_request:                     handle_cancel_request      (req);  break;
-               default: LogMsg("%3d: ERROR: Unsupported UDS req: %d", req->sd, req->hdr.op);
-               }
-
-       // req->msgbuf may be NULL, e.g. for connection_request or remove_record_request
-       if (req->msgbuf) freeL("request_state msgbuf", req->msgbuf);
-
-       // There's no return data for a cancel request (DNSServiceRefDeallocate returns no result)
-       // For a DNSServiceGetProperty call, the handler already generated the response, so no need to do it again here
-       if (req->hdr.op != cancel_request && req->hdr.op != getproperty_request && req->hdr.op != send_bpf)
-               {
-               const mStatus err_netorder = dnssd_htonl(err);
-               send_all(req->errsd, (const char *)&err_netorder, sizeof(err_netorder));
-               if (req->errsd != req->sd)
-                       {
-                       LogOperation("%3d: Error socket %d closed  %08X %08X (%d)",
-                               req->sd, req->errsd, req->hdr.client_context.u32[1], req->hdr.client_context.u32[0], err);
-                       dnssd_close(req->errsd);
-                       req->errsd = req->sd;
-                       // Also need to reset the parent's errsd, if this is a subordinate operation
-                       if (req->primary) req->primary->errsd = req->primary->sd;
-                       }
-               }
-
-       // Reset ready to accept the next req on this pipe
-       if (req->primary) req = req->primary;
-       req->ts         = t_morecoming;
-       req->hdr_bytes  = 0;
-       req->data_bytes = 0;
-       req->msgbuf     = mDNSNULL;
-       req->msgptr     = mDNSNULL;
-       req->msgend     = 0;
+               // req->msgbuf may be NULL, e.g. for connection_request or remove_record_request
+               if (req->msgbuf) freeL("request_state msgbuf", req->msgbuf);
+
+               // There's no return data for a cancel request (DNSServiceRefDeallocate returns no result)
+               // For a DNSServiceGetProperty call, the handler already generated the response, so no need to do it again here
+               if (req->hdr.op != cancel_request && req->hdr.op != getproperty_request && req->hdr.op != send_bpf)
+                       {
+                       const mStatus err_netorder = dnssd_htonl(err);
+                       send_all(req->errsd, (const char *)&err_netorder, sizeof(err_netorder));
+                       if (req->errsd != req->sd)
+                               {
+                               LogOperation("%3d: Error socket %d closed  %08X %08X (%d)",
+                                       req->sd, req->errsd, req->hdr.client_context.u32[1], req->hdr.client_context.u32[0], err);
+                               dnssd_close(req->errsd);
+                               req->errsd = req->sd;
+                               // Also need to reset the parent's errsd, if this is a subordinate operation
+                               if (req->primary) req->primary->errsd = req->primary->sd;
+                               }
+                       }
+
+               // Reset ready to accept the next req on this pipe
+               if (req->primary) req = req->primary;
+               req->ts         = t_morecoming;
+               req->hdr_bytes  = 0;
+               req->data_bytes = 0;
+               req->msgbuf     = mDNSNULL;
+               req->msgptr     = mDNSNULL;
+               req->msgend     = 0;
+               }
        }
 
 mDNSlocal void connect_callback(int fd, short filter, void *info)
@@ -3553,6 +4148,87 @@ mDNSlocal void LogClientInfo(mDNS *const m, const request_state *req)
                LogMsgNoIdent("%s Unrecognized operation %p", prefix, req->terminate);
        }
 
+mDNSlocal char *RecordTypeName(mDNSu8 rtype)
+       {
+       switch (rtype)
+               {
+               case kDNSRecordTypeUnregistered:  return ("Unregistered ");
+               case kDNSRecordTypeDeregistering: return ("Deregistering");
+               case kDNSRecordTypeUnique:        return ("Unique       ");
+               case kDNSRecordTypeAdvisory:      return ("Advisory     ");
+               case kDNSRecordTypeShared:        return ("Shared       ");
+               case kDNSRecordTypeVerified:      return ("Verified     ");
+               case kDNSRecordTypeKnownUnique:   return ("KnownUnique  ");
+               default: return("Unknown");
+               }
+       }
+
+mDNSlocal void LogEtcHosts(mDNS *const m)
+       {
+       mDNSBool showheader = mDNStrue;
+       const AuthRecord *ar;
+       mDNSu32 slot;
+       AuthGroup *ag;
+       int count = 0;
+       int authslot = 0;
+       mDNSBool truncated = 0;
+
+       for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+               {
+               if (m->rrauth.rrauth_hash[slot]) authslot++;
+               for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next)
+                       for (ar = ag->members; ar; ar = ar->next)
+                               {
+                               if (ar->RecordCallback != FreeEtcHosts) continue;
+                               if (showheader) { showheader = mDNSfalse; LogMsgNoIdent("  State       Interface"); }
+               
+                               // Print a maximum of 50 records
+                               if (count++ >= 50) { truncated = mDNStrue; continue; }
+                               if (ar->ARType == AuthRecordLocalOnly)
+                                       {
+                                       if (ar->resrec.InterfaceID == mDNSInterface_LocalOnly)
+                                               LogMsgNoIdent(" %s   LO %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar));
+                                       else
+                                               {
+                                               mDNSu32 scopeid  = (mDNSu32)(uintptr_t)ar->resrec.InterfaceID;
+                                               LogMsgNoIdent(" %s   %u  %s", RecordTypeName(ar->resrec.RecordType), scopeid, ARDisplayString(m, ar));
+                                               }
+                                       }
+                               usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
+                               }
+               }
+
+       if (showheader) LogMsgNoIdent("<None>");
+       else if (truncated) LogMsgNoIdent("<Truncated: to 50 records, Total records %d, Total Auth Groups %d, Auth Slots %d>", count, m->rrauth.rrauth_totalused, authslot);
+       }
+
+mDNSlocal void LogLocalOnlyAuthRecords(mDNS *const m)
+       {
+       mDNSBool showheader = mDNStrue;
+       const AuthRecord *ar;
+       mDNSu32 slot;
+       AuthGroup *ag;
+
+       for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+               {
+               for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next)
+                       for (ar = ag->members; ar; ar = ar->next)
+                               {
+                               if (ar->RecordCallback == FreeEtcHosts) continue;
+                               if (showheader) { showheader = mDNSfalse; LogMsgNoIdent("  State       Interface"); }
+               
+                               // Print a maximum of 400 records
+                               if (ar->ARType == AuthRecordLocalOnly)
+                                       LogMsgNoIdent(" %s   LO %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar));
+                               else if (ar->ARType == AuthRecordP2P)
+                                       LogMsgNoIdent(" %s   PP %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar));
+                               usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
+                               }
+               }
+
+       if (showheader) LogMsgNoIdent("<None>");
+       }
+
 mDNSlocal void LogAuthRecords(mDNS *const m, const mDNSs32 now, AuthRecord *ResourceRecords, int *proxy)
        {
        mDNSBool showheader = mDNStrue;
@@ -3581,9 +4257,9 @@ mDNSlocal void LogAuthRecords(mDNS *const m, const mDNSs32 now, AuthRecord *Reso
                                        (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond,
                                        ar->expire ? (ar->expire - now) / mDNSPlatformOneSecond : 0,
                                        ar->state, ARDisplayString(m, ar));
-                       else if (ar->resrec.InterfaceID == mDNSInterface_LocalOnly)
+                       else if (ar->ARType == AuthRecordLocalOnly)
                                LogMsgNoIdent("                             LO %s", ARDisplayString(m, ar));
-                       else if (ar->resrec.InterfaceID == mDNSInterface_P2P)
+                       else if (ar->ARType == AuthRecordP2P)
                                LogMsgNoIdent("                             PP %s", ARDisplayString(m, ar));
                        else
                                LogMsgNoIdent("%7d %7d %7d %7s %s",
@@ -3649,6 +4325,12 @@ mDNSexport void udsserver_info(mDNS *const m)
        LogMsgNoIdent("--------- Auth Records ---------");
        LogAuthRecords(m, now, m->ResourceRecords, mDNSNULL);
 
+       LogMsgNoIdent("--------- LocalOnly, P2P Auth Records ---------");
+       LogLocalOnlyAuthRecords(m);
+
+       LogMsgNoIdent("--------- /etc/hosts ---------");
+       LogEtcHosts(m);
+
        LogMsgNoIdent("------ Duplicate Records -------");
        LogAuthRecords(m, now, m->DuplicateRecords, mDNSNULL);
 
@@ -3736,7 +4418,7 @@ mDNSexport void udsserver_info(mDNS *const m)
                {
                const DomainAuthInfo *a;
                for (a = m->AuthInfoList; a; a = a->next)
-                       LogMsgNoIdent("%##s %##s%s", a->domain.c, a->keyname.c, a->AutoTunnel ? " AutoTunnel" : "");
+                       LogMsgNoIdent("%##s %##s %##s %d %s", a->domain.c, a->keyname.c, a->hostname.c, (a->port.b[0] << 8 | a->port.b[1]), a->AutoTunnel ? a->AutoTunnel : "");
                }
 
        #if APPLE_OSX_mDNSResponder
@@ -3746,8 +4428,8 @@ mDNSexport void udsserver_info(mDNS *const m)
                {
                const ClientTunnel *c;
                for (c = m->TunnelClients; c; c = c->next)
-                       LogMsgNoIdent("%##s local %.16a %.4a %.16a remote %.16a %.4a %5d %.16a interval %d",
-                               c->dstname.c, &c->loc_inner, &c->loc_outer, &c->loc_outer6, &c->rmt_inner, &c->rmt_outer, mDNSVal16(c->rmt_outer_port), &c->rmt_outer6, c->q.ThisQInterval);
+                       LogMsgNoIdent("%s %##s local %.16a %.4a %.16a remote %.16a %.4a %5d %.16a interval %d",
+                               c->prefix, c->dstname.c, &c->loc_inner, &c->loc_outer, &c->loc_outer6, &c->rmt_inner, &c->rmt_outer, mDNSVal16(c->rmt_outer_port), &c->rmt_outer6, c->q.ThisQInterval);
                }
        #endif // APPLE_OSX_mDNSResponder
 
@@ -3776,15 +4458,17 @@ mDNSexport void udsserver_info(mDNS *const m)
        if (!AutoRegistrationDomains) LogMsgNoIdent("<None>");
        else for (d=AutoRegistrationDomains; d; d=d->next) LogMsgNoIdent("%##s", d->name.c);
 
-       LogMsgNoIdent("--- Search Domains --");
-       if (!SearchList) LogMsgNoIdent("<None>");
-       else
-               {
-               for (s=SearchList; s; s=s->next)
-                       {
-                       LogMsgNoIdent("%##s", s->domain.c);
-                       }
-               }
+       LogMsgNoIdent("--- Search Domains --");
+       if (!SearchList) LogMsgNoIdent("<None>");
+       else
+               {
+               for (s=SearchList; s; s=s->next)
+                       {
+                       char *ifname = InterfaceNameForID(m, s->InterfaceID);
+                       LogMsgNoIdent("%##s %s", s->domain.c, ifname ? ifname : "");
+                       }
+               }
+
        LogMsgNoIdent("---- Task Scheduling Timers ----");
 
        if (!m->NewQuestions)
@@ -3807,6 +4491,9 @@ mDNSexport void udsserver_info(mDNS *const m)
 
        LogMsgNoIdent("SPSProxyListChanged%s", m->SPSProxyListChanged ? "" : " <NONE>");
        LogMsgNoIdent("LocalRemoveEvents%s",   m->LocalRemoveEvents   ? "" : " <NONE>");
+       LogMsgNoIdent("m->RegisterAutoTunnel6  %08X", m->RegisterAutoTunnel6);
+       LogMsgNoIdent("m->AutoTunnelRelayAddrIn  %.16a", &m->AutoTunnelRelayAddrIn);
+       LogMsgNoIdent("m->AutoTunnelRelayAddrOut  %.16a", &m->AutoTunnelRelayAddrOut);
 
 #define LogTimer(MSG,T) LogMsgNoIdent( MSG " %08X %11d  %08X %11d", (T), (T), (T)-now, (T)-now)
 
@@ -3836,9 +4523,7 @@ mDNSexport void udsserver_info(mDNS *const m)
        LogTimer("m->ProbeFailTime        ", m->ProbeFailTime);
        LogTimer("m->DelaySleep           ", m->DelaySleep);
        LogTimer("m->SleepLimit           ", m->SleepLimit);
-       LogMsgNoIdent("m->RegisterAutoTunnel6  %08X", m->RegisterAutoTunnel6);
-       LogMsgNoIdent("m->AutoTunnelRelayAddrIn  %.16a", &m->AutoTunnelRelayAddrIn);
-       LogMsgNoIdent("m->AutoTunnelRelayAddrOut  %.16a", &m->AutoTunnelRelayAddrOut);
+       LogTimer("m->NextScheduledStopTime ", m->NextScheduledStopTime);
        }
 
 #if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING
@@ -4022,10 +4707,10 @@ struct CompileTimeAssertionChecks_uds_daemon
        // Check our structures are reasonable sizes. Including overly-large buffers, or embedding
        // other overly-large structures instead of having a pointer to them, can inadvertently
        // cause structure sizes (and therefore memory usage) to balloon unreasonably.
-       char sizecheck_request_state          [(sizeof(request_state)           <= 2000) ? 1 : -1];
+       char sizecheck_request_state          [(sizeof(request_state)           <= 1784) ? 1 : -1];
        char sizecheck_registered_record_entry[(sizeof(registered_record_entry) <=   60) ? 1 : -1];
        char sizecheck_service_instance       [(sizeof(service_instance)        <= 6552) ? 1 : -1];
-       char sizecheck_browser_t              [(sizeof(browser_t)               <=  1026) ? 1 : -1];
+       char sizecheck_browser_t              [(sizeof(browser_t)               <= 1050) ? 1 : -1];
        char sizecheck_reply_hdr              [(sizeof(reply_hdr)               <=   12) ? 1 : -1];
        char sizecheck_reply_state            [(sizeof(reply_state)             <=   64) ? 1 : -1];
        };
index 93cfd1e96598314da30bb7aa39479fcfb22a853a..1e17efa105395db22486f4a6d9a017fd6698a5ee 100644 (file)
@@ -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);
index 1bfb5ccc77718adb65ee0cf100556b6f0d64e844..814a322ecbdc27f6a2eb86b13daff145ffabec95 100644 (file)
@@ -55,6 +55,7 @@
                                BufferSecurityCheck="true"\r
                                UsePrecompiledHeader="0"\r
                                AssemblerListingLocation="$(IntDir)\"\r
+                               ProgramDataBaseFileName="$(IntDir)\dnssd.dll.pdb"\r
                                WarningLevel="4"\r
                                Detect64BitPortabilityProblems="true"\r
                                DebugInformationFormat="3"\r
@@ -80,7 +81,7 @@
                                LinkIncremental="2"\r
                                ModuleDefinitionFile="dnssd.def"\r
                                GenerateDebugInformation="true"\r
-                               ProgramDatabaseFile="$(OutDir)/dnssd.pdb"\r
+                               ProgramDatabaseFile="$(OutDir)\dnssd.dll.pdb"\r
                                SubSystem="2"\r
                                BaseAddress="0x16000000"\r
                                ImportLibrary="$(OutDir)\dnssd.lib"\r
                                BufferSecurityCheck="true"\r
                                UsePrecompiledHeader="0"\r
                                AssemblerListingLocation="$(IntDir)\"\r
+                               ProgramDataBaseFileName="$(IntDir)\dnssd.dll.pdb"\r
                                WarningLevel="4"\r
                                Detect64BitPortabilityProblems="true"\r
                                DebugInformationFormat="3"\r
                                LinkIncremental="2"\r
                                ModuleDefinitionFile="dnssd.def"\r
                                GenerateDebugInformation="true"\r
-                               ProgramDatabaseFile="$(OutDir)/dnssd.pdb"\r
+                               ProgramDatabaseFile="$(OutDir)\dnssd.dll.pdb"\r
                                SubSystem="2"\r
                                BaseAddress="0x16000000"\r
                                ImportLibrary="$(OutDir)\dnssd.lib"\r
                                RuntimeLibrary="0"\r
                                UsePrecompiledHeader="0"\r
                                AssemblerListingLocation="$(IntDir)\"\r
+                               ProgramDataBaseFileName="$(IntDir)\dnssd.dll.pdb"\r
                                WarningLevel="4"\r
                                Detect64BitPortabilityProblems="true"\r
                                DebugInformationFormat="3"\r
                                LinkIncremental="1"\r
                                ModuleDefinitionFile="dnssd.def"\r
                                GenerateDebugInformation="true"\r
-                               ProgramDatabaseFile="$(IntDir)/$(ProjectName).pdb"\r
+                               ProgramDatabaseFile="$(OutDir)\dnssd.dll.pdb"\r
                                SubSystem="2"\r
                                OptimizeReferences="2"\r
                                EnableCOMDATFolding="2"\r
                        />\r
                        <Tool\r
                                Name="VCPostBuildEventTool"\r
-                               CommandLine="if not &quot;%RC_XBS%&quot; == &quot;YES&quot; goto END&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\WINDOWS\system32\$(PlatformName)&quot;             mkdir &quot;$(DSTROOT)\WINDOWS\system32\$(PlatformName)&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour SDK\include&quot;                      mkdir &quot;$(DSTROOT)\Program Files\Bonjour SDK\include&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(TargetPath)&quot;                                                                            &quot;$(DSTROOT)\WINDOWS\system32\$(PlatformName)&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(ProjectDir)..\..\mDNSShared\dns_sd.h&quot;                                 &quot;$(DSTROOT)\Program Files\Bonjour SDK\include&quot;&#x0D;&#x0A;:END&#x0D;&#x0A;"\r
+                               CommandLine="if not &quot;%RC_XBS%&quot; == &quot;YES&quot; goto END&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\WINDOWS\system32\$(PlatformName)&quot;                     mkdir &quot;$(DSTROOT)\WINDOWS\system32\$(PlatformName)&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib&quot;                                     mkdir &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)&quot;     mkdir &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour SDK\include&quot;                            mkdir &quot;$(DSTROOT)\Program Files\Bonjour SDK\include&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\AppleInternal&quot;                                                                   mkdir &quot;$(DSTROOT)\AppleInternal&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\AppleInternal\bin&quot;                                                           mkdir &quot;$(DSTROOT)\AppleInternal\bin&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\AppleInternal\bin\$(PlatformName)&quot;                           mkdir &quot;$(DSTROOT)\AppleInternal\bin\$(PlatformName)&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(TargetPath)&quot;                                                                                           &quot;$(DSTROOT)\WINDOWS\system32\$(PlatformName)&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(OutDir)\dnssd.dll.pdb&quot;                                                                          &quot;$(DSTROOT)\AppleInternal\bin\$(PlatformName)&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(ProjectDir)..\..\mDNSShared\dns_sd.h&quot;                                             &quot;$(DSTROOT)\Program Files\Bonjour SDK\include&quot;&#x0D;&#x0A;:END&#x0D;&#x0A;"\r
                        />\r
                </Configuration>\r
                <Configuration\r
                                RuntimeLibrary="0"\r
                                UsePrecompiledHeader="0"\r
                                AssemblerListingLocation="$(IntDir)\"\r
+                               ProgramDataBaseFileName="$(IntDir)\dnssd.dll.pdb"\r
                                WarningLevel="4"\r
                                Detect64BitPortabilityProblems="true"\r
                                DebugInformationFormat="3"\r
                                LinkIncremental="1"\r
                                ModuleDefinitionFile="dnssd.def"\r
                                GenerateDebugInformation="true"\r
-                               ProgramDatabaseFile="$(IntDir)/$(ProjectName).pdb"\r
+                               ProgramDatabaseFile="$(OutDir)\dnssd.dll.pdb"\r
                                SubSystem="2"\r
                                OptimizeReferences="2"\r
                                EnableCOMDATFolding="2"\r
                        />\r
                        <Tool\r
                                Name="VCPostBuildEventTool"\r
-                               CommandLine="if not &quot;%RC_XBS%&quot; == &quot;YES&quot; goto END&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\WINDOWS\system32\$(PlatformName)&quot;             mkdir &quot;$(DSTROOT)\WINDOWS\system32\$(PlatformName)&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour SDK\include&quot;                      mkdir &quot;$(DSTROOT)\Program Files\Bonjour SDK\include&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(TargetPath)&quot;                                                                            &quot;$(DSTROOT)\WINDOWS\system32\$(PlatformName)&quot;&#x0D;&#x0A;:END&#x0D;&#x0A;"\r
+                               CommandLine="if not &quot;%RC_XBS%&quot; == &quot;YES&quot; goto END&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\WINDOWS\system32\$(PlatformName)&quot;                     mkdir &quot;$(DSTROOT)\WINDOWS\system32\$(PlatformName)&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib&quot;                                     mkdir &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)&quot;     mkdir &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour SDK\include&quot;                            mkdir &quot;$(DSTROOT)\Program Files\Bonjour SDK\include&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\AppleInternal&quot;                                                                   mkdir &quot;$(DSTROOT)\AppleInternal&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\AppleInternal\bin&quot;                                                           mkdir &quot;$(DSTROOT)\AppleInternal\bin&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\AppleInternal\bin\$(PlatformName)&quot;                           mkdir &quot;$(DSTROOT)\AppleInternal\bin\$(PlatformName)&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(TargetPath)&quot;                                                                                           &quot;$(DSTROOT)\WINDOWS\system32\$(PlatformName)&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(OutDir)\dnssd.dll.pdb&quot;                                                                         &quot;$(DSTROOT)\AppleInternal\bin\$(PlatformName)&quot;&#x0D;&#x0A;:END&#x0D;&#x0A;"\r
                        />\r
                </Configuration>\r
        </Configurations>\r
index 8029219ed524ac7b613f34f460b5a027f6f40055..dc6828924fabe192f389537ef349245305df43ab 100755 (executable)
@@ -51,6 +51,7 @@
                                BasicRuntimeChecks="3"\r
                                RuntimeLibrary="1"\r
                                UsePrecompiledHeader="0"\r
+                               ProgramDataBaseFileName="$(IntDir)\dnssd.pdb"\r
                                WarningLevel="3"\r
                                Detect64BitPortabilityProblems="true"\r
                                DebugInformationFormat="3"\r
                                BasicRuntimeChecks="3"\r
                                RuntimeLibrary="1"\r
                                UsePrecompiledHeader="0"\r
+                               ProgramDataBaseFileName="$(IntDir)\dnssd.pdb"\r
                                WarningLevel="3"\r
                                Detect64BitPortabilityProblems="true"\r
                                DebugInformationFormat="3"\r
                                PreprocessorDefinitions="WIN32;NDEBUG;_LIB;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1"\r
                                RuntimeLibrary="0"\r
                                UsePrecompiledHeader="0"\r
+                               ProgramDataBaseFileName="$(IntDir)\dnssd.lib.pdb"\r
                                WarningLevel="3"\r
                                Detect64BitPortabilityProblems="true"\r
                                DebugInformationFormat="3"\r
                        />\r
                        <Tool\r
                                Name="VCPostBuildEventTool"\r
-                               CommandLine="if not &quot;%RC_XBS%&quot; == &quot;YES&quot; goto END&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)&quot; mkdir &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)&quot;&#x0D;&#x0A;echo F | xcopy /Y &quot;$(OutDir)\dnssdStatic.lib&quot;                                                        &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)\dnssd.lib&quot;&#x0D;&#x0A;:END&#x0D;&#x0A;"\r
+                               CommandLine="if not &quot;%RC_XBS%&quot; == &quot;YES&quot; goto END&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)&quot; mkdir &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)&quot;&#x0D;&#x0A;echo F | xcopy /Y &quot;$(OutDir)\dnssdStatic.lib&quot;                                                        &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)\dnssd.lib&quot;&#x0D;&#x0A;xcopy /Y &quot;$(OutDir)\dnssd.lib.pdb&quot;                                                                         &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)&quot;&#x0D;&#x0A;:END&#x0D;&#x0A;"\r
                        />\r
                </Configuration>\r
                <Configuration\r
                                PreprocessorDefinitions="WIN32;NDEBUG;_LIB;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1"\r
                                RuntimeLibrary="0"\r
                                UsePrecompiledHeader="0"\r
+                               ProgramDataBaseFileName="$(IntDir)\dnssd.lib.pdb"\r
                                WarningLevel="3"\r
                                Detect64BitPortabilityProblems="true"\r
                                DebugInformationFormat="3"\r
                        />\r
                        <Tool\r
                                Name="VCPostBuildEventTool"\r
-                               CommandLine="if not &quot;%RC_XBS%&quot; == &quot;YES&quot; goto END&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)&quot; mkdir &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)&quot;&#x0D;&#x0A;echo F | xcopy /I/Y &quot;$(OutDir)\dnssdStatic.lib&quot;                                                     &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)\dnssd.lib&quot;&#x0D;&#x0A;:END&#x0D;&#x0A;"\r
+                               CommandLine="if not &quot;%RC_XBS%&quot; == &quot;YES&quot; goto END&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)&quot; mkdir &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)&quot;&#x0D;&#x0A;echo F | xcopy /I/Y &quot;$(OutDir)\dnssdStatic.lib&quot;                                                     &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)\dnssd.lib&quot;&#x0D;&#x0A;xcopy /Y &quot;$(OutDir)\dnssd.lib.pdb&quot;                                                                         &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)&quot;&#x0D;&#x0A;:END&#x0D;&#x0A;"\r
                        />\r
                </Configuration>\r
        </Configurations>\r
index 2ef5d212beb4b9d0d8dba28d5bd832f2cf445623..8b4c2c6f5110df7479016da40d397be4fea5e1f3 100644 (file)
@@ -1189,6 +1189,7 @@ static OSStatus   ServiceSpecificInitialize( int argc, LPTSTR argv[] )
 
        gPlatformStorage.registerWaitableEventFunc = RegisterWaitableEvent;
        gPlatformStorage.unregisterWaitableEventFunc = UnregisterWaitableEvent;
+       gPlatformStorage.reportStatusFunc = ReportStatus;
 
        err = mDNS_Init( &gMDNSRecord, &gPlatformStorage, gRRCache, RR_CACHE_SIZE, mDNS_Init_AdvertiseLocalAddresses, CoreCallback, mDNS_Init_NoInitCallbackContext); 
        require_noerr( err, exit);
@@ -1305,11 +1306,27 @@ static OSStatus ServiceSpecificRun( int argc, LPTSTR argv[] )
                        }
 
                        // Wait until something occurs (e.g. cancel, incoming packet, or timeout).
+                       //
+                       // Note: There seems to be a bug in WinSock with respect to Alertable I/O. According
+                       // to MSDN <http://msdn.microsoft.com/en-us/library/aa363772(VS.85).aspx>, Alertable I/O
+                       // callbacks will only be invoked during the following calls (when the caller sets
+                       // the appropriate flag):
+                       //
+                       // - SleepEx
+                       // - WaitForSingleObjectEx
+                       // - WaitForMultipleObjectsEx
+                       // - SignalObjectAndWait
+                       // - MsgWaitForMultipleObjectsEx
+                       //
+                       // However, we have seen callbacks be invoked during calls to bind() (and maybe others). If there
+                       // weren't a bug, then socket events would only be queued during the call to WaitForMultipleObjects() and
+                       // we'd only have to check them once afterwards. However since that doesn't seem to be the case, we'll
+                       // check the queue both before we call WaitForMultipleObjects() and after.
 
-                       SetSocketEventsEnabled( &gMDNSRecord, TRUE );
+                       DispatchSocketEvents( &gMDNSRecord );
                        result = WaitForMultipleObjectsEx( ( DWORD ) waitListCount, waitList, FALSE, (DWORD) nextTimerEvent, TRUE );
-                       SetSocketEventsEnabled( &gMDNSRecord, FALSE );
                        check( result != WAIT_FAILED );
+                       DispatchSocketEvents( &gMDNSRecord );
 
                        if ( result != WAIT_FAILED )
                        {
@@ -2061,6 +2078,7 @@ mDNSlocal void UDSCanRead( TCPSocket * sock )
 //     udsSupportAddFDToEventLoop
 //===========================================================================================================================
 
+
 mStatus
 udsSupportAddFDToEventLoop( SocketRef fd, udsEventCallback callback, void *context, void **platform_data)
 {
index 7461028b1516bba6efbb05bed05612ad26a5cac3..720f00aceaffcea123b54fdeb747e350695e558f 100644 (file)
@@ -44,7 +44,7 @@
                        <Tool\r
                                Name="VCCLCompilerTool"\r
                                Optimization="0"\r
-                               AdditionalIncludeDirectories=".;../;../../mDNSCore;../../mDNSShared;&quot;C:/Program Files/Microsoft SDKs/Windows/v6.1/Include&quot;"\r
+                               AdditionalIncludeDirectories=".;../;../../mDNSCore;../../mDNSShared;&quot;$(VCInstallDir)include&quot;;&quot;$(VCInstallDir)atlmfc\include&quot;;&quot;C:/Program Files/Microsoft SDKs/Windows/v6.1/Include&quot;"\r
                                PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;_WIN32_WINNT=0x0501;DEBUG=1;MDNS_DEBUGMSGS=0;TARGET_OS_WIN32;WIN32_LEAN_AND_MEAN;USE_TCP_LOOPBACK;PLATFORM_NO_STRSEP;PLATFORM_NO_EPIPE;PLATFORM_NO_RLIMIT;PID_FILE=&quot;&quot;&quot;&quot;;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1;_LEGACY_NAT_TRAVERSAL_;_USE_32BIT_TIME_T"\r
                                StringPooling="true"\r
                                MinimalRebuild="true"\r
                        <Tool\r
                                Name="VCCLCompilerTool"\r
                                Optimization="0"\r
-                               AdditionalIncludeDirectories=".;../;../../mDNSCore;../../mDNSShared;&quot;C:/Program Files/Microsoft SDKs/Windows/v6.1/Include&quot;"\r
+                               AdditionalIncludeDirectories=".;../;../../mDNSCore;../../mDNSShared;&quot;$(VCInstallDir)include&quot;;&quot;$(VCInstallDir)atlmfc\include&quot;;&quot;C:/Program Files/Microsoft SDKs/Windows/v6.1/Include&quot;"\r
                                PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;_WIN32_WINNT=0x0501;DEBUG=1;MDNS_DEBUGMSGS=0;TARGET_OS_WIN32;WIN32_LEAN_AND_MEAN;USE_TCP_LOOPBACK;PLATFORM_NO_STRSEP;PLATFORM_NO_EPIPE;PLATFORM_NO_RLIMIT;PID_FILE=&quot;&quot;&quot;&quot;;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1;_LEGACY_NAT_TRAVERSAL_"\r
                                StringPooling="true"\r
                                MinimalRebuild="true"\r
                        />\r
                        <Tool\r
                                Name="VCCLCompilerTool"\r
-                               AdditionalIncludeDirectories=".;../;../../mDNSCore;../../mDNSShared;&quot;C:/Program Files/Microsoft SDKs/Windows/v6.1/Include&quot;"\r
+                               AdditionalIncludeDirectories=".;../;../../mDNSCore;../../mDNSShared;&quot;$(VCInstallDir)include&quot;;&quot;$(VCInstallDir)atlmfc\include&quot;;&quot;C:/Program Files/Microsoft SDKs/Windows/v6.1/Include&quot;"\r
                                PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_WIN32_WINNT=0x0501;TARGET_OS_WIN32;WIN32_LEAN_AND_MEAN;USE_TCP_LOOPBACK;PLATFORM_NO_STRSEP;PLATFORM_NO_EPIPE;PLATFORM_NO_RLIMIT;PID_FILE=&quot;&quot;&quot;&quot;;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1;_LEGACY_NAT_TRAVERSAL_;_USE_32BIT_TIME_T"\r
                                RuntimeLibrary="0"\r
                                UsePrecompiledHeader="0"\r
                        />\r
                        <Tool\r
                                Name="VCCLCompilerTool"\r
-                               AdditionalIncludeDirectories=".;../;../../mDNSCore;../../mDNSShared;&quot;C:/Program Files/Microsoft SDKs/Windows/v6.1/Include&quot;"\r
+                               AdditionalIncludeDirectories=".;../;../../mDNSCore;../../mDNSShared;&quot;$(VCInstallDir)include&quot;;&quot;$(VCInstallDir)atlmfc\include&quot;;&quot;C:/Program Files/Microsoft SDKs/Windows/v6.1/Include&quot;"\r
                                PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_WIN32_WINNT=0x0501;TARGET_OS_WIN32;WIN32_LEAN_AND_MEAN;USE_TCP_LOOPBACK;PLATFORM_NO_STRSEP;PLATFORM_NO_EPIPE;PLATFORM_NO_RLIMIT;PID_FILE=&quot;&quot;&quot;&quot;;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1;_LEGACY_NAT_TRAVERSAL_"\r
                                RuntimeLibrary="0"\r
                                UsePrecompiledHeader="0"\r
index 58feb7fb93bd827ba6da689125f77f037754ad44..179ba783e1c8508ffd4a04c1dedae2c4582fb468 100755 (executable)
@@ -142,9 +142,11 @@ mDNSlocal void                             TCPDidConnect( mDNS * const inMDNS, HANDLE event, void * conte
 mDNSlocal void                         TCPCanRead( TCPSocket * sock );
 mDNSlocal mStatus                      TCPBeginRecv( TCPSocket * sock );
 mDNSlocal void CALLBACK                TCPEndRecv( DWORD error, DWORD bytesTransferred, LPWSAOVERLAPPED overlapped, DWORD flags );
+mDNSlocal void                         TCPCloseSocket( TCPSocket * socket );
 mDNSlocal void CALLBACK                TCPFreeSocket( TCPSocket *sock );
 mDNSlocal OSStatus                     UDPBeginRecv( UDPSocket * socket );
 mDNSlocal void CALLBACK                UDPEndRecv( DWORD err, DWORD bytesTransferred, LPWSAOVERLAPPED overlapped, DWORD flags );
+mDNSlocal void                         UDPCloseSocket( UDPSocket * sock );
 mDNSlocal void CALLBACK                UDPFreeSocket( UDPSocket * sock );
 mDNSlocal mStatus           SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa);
 mDNSlocal void                         GetDDNSFQDN( domainname *const fqdn );
@@ -159,10 +161,8 @@ mDNSlocal VOID CALLBACK            CheckFileSharesProc( LPVOID arg, DWORD dwTimerLowValue,
 mDNSlocal void                         CheckFileShares( mDNS * const inMDNS );
 mDNSlocal void                         SMBCallback(mDNS *const m, ServiceRecordSet *const srs, mStatus result);
 mDNSlocal mDNSu8                       IsWOMPEnabledForAdapter( const char * adapterName );
-mDNSlocal void                         FreeSocketEventsForSocket( mDNS * const inMDNS, void * sock );
-mDNSlocal void                         FreeSocketEvents( mDNS * const inMDNS );
-mDNSlocal void                         TCPSocketEventHandler( mDNS * const inMDNS, void * v );
-mDNSlocal void                         UDPSocketEventHandler( mDNS * const inMDNS, void * v );
+mDNSlocal void                         DispatchUDPEvent( mDNS * const m, UDPSocket * sock );
+mDNSlocal void                         DispatchTCPEvent( mDNS * const m, TCPSocket * sock );
 
 #ifdef __cplusplus
        }
@@ -177,11 +177,11 @@ mDNSlocal void                            UDPSocketEventHandler( mDNS * const inMDNS, void * v );
 //===========================================================================================================================
 
 mDNSlocal mDNS_PlatformSupport gMDNSPlatformSupport;
-mDNSs32                                                        mDNSPlatformOneSecond = 0;
-mDNSlocal UDPSocket            *               gUDPSocketList                  = NULL;
-mDNSlocal int                                  gUDPSockets                             = 0;
-mDNSlocal BOOL                                 gSocketEventsEnabled    = FALSE;
-mDNSlocal GenLinkedList                        gSocketEvents;
+mDNSs32                                                        mDNSPlatformOneSecond   = 0;
+mDNSlocal UDPSocket            *               gUDPSockets                             = NULL;
+mDNSlocal int                                  gUDPNumSockets                  = 0;
+mDNSlocal GenLinkedList                        gUDPDispatchableSockets;
+mDNSlocal GenLinkedList                        gTCPDispatchableSockets;
 
 #if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS )
 
@@ -294,7 +294,8 @@ mDNSexport mStatus  mDNSPlatformInit( mDNS * const inMDNS )
        require_action( inMDNS->p->checkFileSharesTimer, exit, err = mStatus_UnknownErr );
        inMDNS->p->checkFileSharesTimeout               = 10;           // Retry time for CheckFileShares() in seconds
        mDNSPlatformOneSecond                                   = 1000;         // Use milliseconds as the quantum of time
-       InitLinkedList( &gSocketEvents, offsetof( SocketEvent, next));
+       InitLinkedList( &gTCPDispatchableSockets, offsetof( TCPSocket, nextDispatchable ) );
+       InitLinkedList( &gUDPDispatchableSockets, offsetof( UDPSocket, nextDispatchable ) );
        
        // Startup WinSock 2.2 or later.
        
@@ -333,6 +334,7 @@ mDNSexport mStatus  mDNSPlatformInit( mDNS * const inMDNS )
        inMDNS->p->unicastSock4.fd                      = INVALID_SOCKET;
        inMDNS->p->unicastSock4.recvMsgPtr      = NULL;
        inMDNS->p->unicastSock4.ifd                     = NULL;
+       inMDNS->p->unicastSock4.overlapped.pending = FALSE;
        inMDNS->p->unicastSock4.next            = NULL;
        inMDNS->p->unicastSock4.m                       = inMDNS;
 
@@ -364,6 +366,7 @@ mDNSexport mStatus  mDNSPlatformInit( mDNS * const inMDNS )
        inMDNS->p->unicastSock6.fd                      = INVALID_SOCKET;
        inMDNS->p->unicastSock6.recvMsgPtr      = NULL;
        inMDNS->p->unicastSock6.ifd                     = NULL;
+       inMDNS->p->unicastSock6.overlapped.pending = FALSE;
        inMDNS->p->unicastSock6.next            = NULL;
        inMDNS->p->unicastSock6.m                       = inMDNS;
 
@@ -488,21 +491,13 @@ mDNSexport void   mDNSPlatformClose( mDNS * const inMDNS )
 
 #if ( MDNS_WINDOWS_ENABLE_IPV4 )
 
-       if ( inMDNS->p->unicastSock4.fd != INVALID_SOCKET )
-       {
-               closesocket( inMDNS->p->unicastSock4.fd );
-               inMDNS->p->unicastSock4.fd = INVALID_SOCKET;
-       }
+       UDPCloseSocket( &inMDNS->p->unicastSock4 );
 
 #endif
        
 #if ( MDNS_WINDOWS_ENABLE_IPV6 )
 
-       if ( inMDNS->p->unicastSock6.fd != INVALID_SOCKET )
-       {
-               closesocket( inMDNS->p->unicastSock6.fd );
-               inMDNS->p->unicastSock6.fd = INVALID_SOCKET;
-       }
+       UDPCloseSocket( &inMDNS->p->unicastSock6 );
 
 #endif
 
@@ -542,9 +537,10 @@ mDNSexport void    mDNSPlatformClose( mDNS * const inMDNS )
 
        // Clear out the APC queue
 
-       SetSocketEventsEnabled( inMDNS, TRUE );
-       while ( SleepEx( 0, TRUE ) == WAIT_IO_COMPLETION );
-       SetSocketEventsEnabled( inMDNS, FALSE );
+       while ( SleepEx( 0, TRUE ) == WAIT_IO_COMPLETION )
+       {
+               DispatchSocketEvents( inMDNS );
+       }
 
        WSACleanup();
        
@@ -872,9 +868,10 @@ mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex( mDNS * con
 //     mDNSPlatformInterfaceIndexfromInterfaceID
 //===========================================================================================================================
        
-mDNSexport mDNSu32     mDNSPlatformInterfaceIndexfromInterfaceID( mDNS * const inMDNS, mDNSInterfaceID inID )
+mDNSexport mDNSu32     mDNSPlatformInterfaceIndexfromInterfaceID( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSBool suppressNetworkChange )
 {
        mDNSu32         index;
+       (void) suppressNetworkChange; // Unused
        
        index = 0;
        if( inID == mDNSInterface_LocalOnly )
@@ -1013,10 +1010,10 @@ mDNSPlatformTCPConnect
        )
 {
        struct sockaddr_in      saddr;
-       ( void ) hostname; 
        mStatus                         err             = mStatus_NoError;
 
        DEBUG_UNUSED( inInterfaceID );
+       ( void ) hostname;
 
        if ( inDstIP->type != mDNSAddrType_IPv4 )
        {
@@ -1118,15 +1115,13 @@ mDNSexport void mDNSPlatformTCPCloseConnection( TCPSocket *sock )
 
        if ( sock->fd != INVALID_SOCKET )
        {
-               closesocket( sock->fd );
-               sock->fd = INVALID_SOCKET;
+               TCPCloseSocket( sock );
 
                QueueUserAPC( ( PAPCFUNC ) TCPFreeSocket, sock->m->p->mainThread, ( ULONG_PTR ) sock );
        }
-
-       FreeSocketEventsForSocket( sock->m, sock );
 }
 
+
 //===========================================================================================================================
 //     mDNSPlatformReadTCP
 //===========================================================================================================================
@@ -1158,26 +1153,13 @@ mDNSexport long mDNSPlatformReadTCP( TCPSocket *sock, void *inBuffer, unsigned l
                        memcpy( inBuffer, sock->bptr, bytesToCopy );
                        sock->bptr += bytesToCopy;
 
-                       ret = bytesToCopy;
-
-                       if ( bytesLeft == bytesToCopy )
+                       if ( !sock->overlapped.pending && ( sock->bptr == sock->eptr ) )
                        {
-                               sock->lastError = TCPBeginRecv( sock );
-                               
-                               // If we can't immediately queue up another read, abort the connection
-                               // now, even if we successfully wrote bytes to the buffer.
-                               // We don't expect this to happen unless something is seriously borked.
-                               // If we run into this in the real world, we should consider queuing up
-                               // a user APC function to defer an explicit callback to the read event handler
-                               // to inform the consumer of the problem.
-
-                               if ( sock->lastError )
-                               {
-                                       dlog( kDebugLevelError, DEBUG_NAME "TCPBeginRecv failed with error %d\n", sock->lastError );
-                                       wsaError = sock->lastError;
-                                       ret = -1;
-                               }
+                               sock->bptr = sock->bbuf;
+                               sock->eptr = sock->bbuf;
                        }
+
+                       ret = bytesToCopy;
                }
                else
                {
@@ -1237,6 +1219,10 @@ mStatus TCPAddSocket( mDNS * const inMDNS, TCPSocket *sock )
 
        ( void ) inMDNS;
 
+       sock->bptr      = sock->bbuf;
+       sock->eptr      = sock->bbuf;
+       sock->ebuf      = sock->bbuf + sizeof( sock->bbuf );
+
        dlog( kDebugLevelChatty, DEBUG_NAME "adding TCPSocket 0x%x:%d\n", sock, sock->fd );
        err = TCPBeginRecv( sock );
        require_noerr( err, exit );
@@ -1320,16 +1306,22 @@ mDNSlocal mStatus TCPBeginRecv( TCPSocket * sock )
        DWORD   flags                   = 0;
        mStatus err;
 
-       ZeroMemory( &sock->overlapped, sizeof( sock->overlapped ) );
-       sock->overlapped.hEvent = sock;
+       dlog( kDebugLevelChatty, DEBUG_NAME "%s: sock = %d\n", __ROUTINE__, sock->fd );
+
+       check( !sock->overlapped.pending );
 
-       sock->wbuf.buf = ( char* ) sock->buf;
-       sock->wbuf.len = sizeof( sock->buf );
+       ZeroMemory( &sock->overlapped.data, sizeof( sock->overlapped.data ) );
+       sock->overlapped.data.hEvent = sock;
 
-       err = WSARecv( sock->fd, &sock->wbuf, 1, &bytesReceived, &flags, &sock->overlapped, ( LPWSAOVERLAPPED_COMPLETION_ROUTINE ) TCPEndRecv );
+       sock->overlapped.wbuf.buf = ( char* ) sock->eptr;
+       sock->overlapped.wbuf.len = ( ULONG) ( sock->ebuf - sock->eptr );
+       
+       err = WSARecv( sock->fd, &sock->overlapped.wbuf, 1, &bytesReceived, &flags, &sock->overlapped.data, ( LPWSAOVERLAPPED_COMPLETION_ROUTINE ) TCPEndRecv );
        err = translate_errno( ( err == 0 ) || ( WSAGetLastError() == WSA_IO_PENDING ), WSAGetLastError(), kUnknownErr );
        require_noerr( err, exit );
 
+       sock->overlapped.pending = TRUE;
+
 exit:
 
        return err;
@@ -1346,83 +1338,25 @@ mDNSlocal void CALLBACK TCPEndRecv( DWORD error, DWORD bytesTransferred, LPWSAOV
 
        ( void ) flags;
 
+       dlog( kDebugLevelChatty, DEBUG_NAME "%s: error = %d, bytesTransferred = %d\n", __ROUTINE__, error, bytesTransferred );
        sock = ( overlapped != NULL ) ? overlapped->hEvent : NULL;
+       require_action( sock, exit, error = ( DWORD ) mStatus_BadStateErr );
+       dlog( kDebugLevelChatty, DEBUG_NAME "%s: sock = %d\n", __ROUTINE__, sock->fd );
+       sock->overlapped.error                          = error;
+       sock->overlapped.bytesTransferred       = bytesTransferred;
+       check( sock->overlapped.pending );
+       sock->overlapped.pending                        = FALSE;
 
-       if ( sock && ( sock->fd != INVALID_SOCKET ) )
-       {
-               // <rdar://problem/7532492> mDNSResponder gets locking errors on Windows
-               //
-               // There seems to be a bug in WinSock with respect to Alertable I/O. According
-               // to MSDN <http://msdn.microsoft.com/en-us/library/aa363772(VS.85).aspx>, Alertable I/O
-               // callbacks will only be invoked during the following calls (when the caller sets
-               // the appropriate flag):
-               //
-               // - SleepEx
-               // - WaitForSingleObjectEx
-               // - WaitForMultipleObjectsEx
-               // - SignalObjectAndWait
-               // - MsgWaitForMultipleObjectsEx
-               //
-               // However, we have seen callbacks be invoked during calls to bind() (and maybe others).
-               // To workaround this, we set the gSocketEventsEnabled flag to be TRUE only when it's okay
-               // to directly call the readEventHandler.  Otherwise we queue the packet up and
-               // dispatch it later.
-
-               if ( gSocketEventsEnabled && !gSocketEvents.Head )
-               {
-                       sock->lastError = error;
-
-                       if ( !error )
-                       {
-                               if ( bytesTransferred )
-                               {
-                                       sock->bptr = sock->buf;
-                                       sock->eptr = sock->buf + bytesTransferred;
-                               }
-                               else
-                               {
-                                       sock->closed = TRUE;
-                               }
-                       }
+       // Queue this socket
 
-                       if ( sock->readEventHandler != NULL )
-                       {
-                               // Turn off socket events before calling the readEventHandler.
-                               // This will prevent reentrancy problems.
-                               //
-                               // Don't forget to reenable socket events afterwards.
-
-                               SetSocketEventsEnabled( sock->m, FALSE );
-                               sock->readEventHandler( sock );
-                               SetSocketEventsEnabled( sock->m, TRUE );
-                       }
-               }
-               else
-               {
-                       TCPSocketEvent * event;
-
-                       event = malloc( sizeof( TCPSocketEvent ) );
-                       require_action( event, exit, error = ( DWORD ) kNoMemoryErr );
-                       event->super.sock               = sock;
-                       event->super.handler    = &TCPSocketEventHandler;
-                       event->super.next               = NULL;
-                       event->error                    = error;
-                       event->bytesTransferred = bytesTransferred;
-
-                       if ( bytesTransferred )
-                       {
-                               memcpy( event->buf, sock->buf, bytesTransferred );  
-                       }
-
-                       AddToTail( &gSocketEvents, event );
-               }
-       }
+       AddToTail( &gTCPDispatchableSockets, sock );
 
 exit:
 
        return;
 }
 
+
        
 //===========================================================================================================================
 //     mDNSPlatformUDPSocket
@@ -1443,11 +1377,12 @@ mDNSexport UDPSocket* mDNSPlatformUDPSocket(mDNS *const m, const mDNSIPPort requ
 
        // Create the socket
 
-       sock->fd                        = INVALID_SOCKET;
-       sock->recvMsgPtr        = m->p->unicastSock4.recvMsgPtr;
-       sock->addr                      = m->p->unicastSock4.addr;
-       sock->ifd                       = NULL;
-       sock->m                         = m;
+       sock->fd                                        = INVALID_SOCKET;
+       sock->recvMsgPtr                        = m->p->unicastSock4.recvMsgPtr;
+       sock->addr                                      = m->p->unicastSock4.addr;
+       sock->ifd                                       = NULL;
+       sock->overlapped.pending        = FALSE;
+       sock->m                                         = m;
 
        // Try at most 10000 times to get a unique random port
 
@@ -1485,9 +1420,9 @@ mDNSexport UDPSocket* mDNSPlatformUDPSocket(mDNS *const m, const mDNSIPPort requ
 
        // Bookkeeping
 
-       sock->next                      = gUDPSocketList;
-       gUDPSocketList          = sock;
-       gUDPSockets++;
+       sock->next              = gUDPSockets;
+       gUDPSockets             = sock;
+       gUDPNumSockets++;
 
 exit:
 
@@ -1506,7 +1441,7 @@ exit:
        
 mDNSexport void mDNSPlatformUDPClose( UDPSocket *sock )
 {
-       UDPSocket       *       current  = gUDPSocketList;
+       UDPSocket       *       current  = gUDPSockets;
        UDPSocket       *       last = NULL;
 
        while ( current )
@@ -1515,7 +1450,7 @@ mDNSexport void mDNSPlatformUDPClose( UDPSocket *sock )
                {
                        if ( last == NULL )
                        {
-                               gUDPSocketList = sock->next;
+                               gUDPSockets = sock->next;
                        }
                        else
                        {
@@ -1533,13 +1468,11 @@ mDNSexport void mDNSPlatformUDPClose( UDPSocket *sock )
                        // UDPEndRecv will check if the socket is valid, and if not, it will ignore
                        // the packet
 
-                       closesocket( sock->fd );
-                       sock->fd = INVALID_SOCKET;
+                       UDPCloseSocket( sock );
 
                        QueueUserAPC( ( PAPCFUNC ) UDPFreeSocket, sock->m->p->mainThread, ( ULONG_PTR ) sock );
-                       FreeSocketEventsForSocket( sock->m, sock );
 
-                       gUDPSockets--;
+                       gUDPNumSockets--;
 
                        break;
                }
@@ -1646,6 +1579,13 @@ mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID
 //     mDNSPlatformSendRawPacket
 //===========================================================================================================================
        
+mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason)\r
+    {\r
+    DEBUG_UNUSED( m );\r
+       DEBUG_UNUSED( allowSleep );\r
+       DEBUG_UNUSED( reason );\r
+    }\r
+
 mDNSexport void mDNSPlatformSendRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID)
        {
        DEBUG_UNUSED( msg );
@@ -1662,6 +1602,7 @@ mDNSexport void mDNSPlatformReceiveRawPacket(const void *const msg, const mDNSu8
 
 mDNSexport void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSAddr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID)
        {
+       DEBUG_UNUSED( m );
        DEBUG_UNUSED( tpa );
        DEBUG_UNUSED( tha );
        DEBUG_UNUSED( InterfaceID );
@@ -1796,6 +1737,15 @@ exit:
 }
 
 
+mDNSexport void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result)
+    {
+    (void)m;  // unused
+    (void)rr;
+    (void)result;
+    }
+
+
+
 //===========================================================================================================================
 //     SetDomainSecrets
 //===========================================================================================================================
@@ -1868,7 +1818,7 @@ SetSearchDomainList( void )
        while ( tok )
        {
                if ( ( strcmp( tok, "" ) != 0 ) && ( strcmp( tok, "." ) != 0 ) )
-                       mDNS_AddSearchDomain_CString(tok);
+                       mDNS_AddSearchDomain_CString(tok, mDNSNULL);
                tok = strtok( NULL, "," );
        }
 
@@ -1914,7 +1864,7 @@ SetReverseMapSearchDomainList( void )
                                                              addr.ip.v4.b[2] & netmask.ip.v4.b[2],
                                                              addr.ip.v4.b[1] & netmask.ip.v4.b[1],
                                                              addr.ip.v4.b[0] & netmask.ip.v4.b[0]);
-                               mDNS_AddSearchDomain_CString(buffer);
+                               mDNS_AddSearchDomain_CString(buffer, mDNSNULL);
                        }
                }
        
@@ -2001,7 +1951,7 @@ SetDNSServers( mDNS *const m )
        {
                mDNSAddr addr;
                err = StringToAddress( &addr, ipAddr->IpAddress.String );
-               if ( !err ) mDNS_AddDNSServer(m, mDNSNULL, mDNSInterface_Any, &addr, UnicastDNSPort);
+               if ( !err ) mDNS_AddDNSServer(m, mDNSNULL, mDNSInterface_Any, &addr, UnicastDNSPort, mDNSfalse, 0);
        }
 
 exit:
@@ -2086,7 +2036,7 @@ SetDomainFromDHCP( void )
                                check_noerr( err );
                        }
 
-                       if ( domain && domain[0] ) mDNS_AddSearchDomain_CString(domain);
+                       if ( domain && domain[0] ) mDNS_AddSearchDomain_CString(domain, mDNSNULL);
 
                        break;
                }
@@ -2192,6 +2142,15 @@ mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID Inte
        (void) iteration;
        }
 
+mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf)
+       {
+       (void) rr;
+       (void) intf;
+
+       return 1;
+       }
+
+
 #if 0
 #pragma mark -
 #endif
@@ -2694,10 +2653,6 @@ mStatus  TearDownInterfaceList( mDNS * const inMDNS )
        check( inMDNS );
        check( inMDNS->p );
 
-       // Free any pending events received.
-
-       FreeSocketEvents( inMDNS );
-       
        // Free any interfaces that were previously marked inactive and are no longer referenced by the mDNS cache.
        // Interfaces are marked inactive, but not deleted immediately if they were still referenced by the mDNS cache
        // so that remove events that occur after an interface goes away can still report the correct interface.
@@ -2755,12 +2710,13 @@ mDNSlocal mStatus       SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inI
        
        ifd = (mDNSInterfaceData *) calloc( 1, sizeof( *ifd ) );
        require_action( ifd, exit, err = mStatus_NoMemoryErr );
-       ifd->sock.fd    = kInvalidSocketRef;
-       ifd->sock.ifd   = ifd;
-       ifd->sock.next  = NULL;
-       ifd->sock.m             = inMDNS;
-       ifd->index              = inIFA->ifa_extra.index;
-       ifd->scopeID    = inIFA->ifa_extra.index;
+       ifd->sock.fd            = kInvalidSocketRef;
+       ifd->sock.overlapped.pending = FALSE;
+       ifd->sock.ifd           = ifd;
+       ifd->sock.next          = NULL;
+       ifd->sock.m                     = inMDNS;
+       ifd->index                      = inIFA->ifa_extra.index;
+       ifd->scopeID            = inIFA->ifa_extra.index;
        check( strlen( inIFA->ifa_name ) < sizeof( ifd->name ) );
        strncpy( ifd->name, inIFA->ifa_name, sizeof( ifd->name ) - 1 );
        ifd->name[ sizeof( ifd->name ) - 1 ] = '\0';
@@ -2891,12 +2847,8 @@ mDNSlocal mStatus        TearDownInterface( mDNS * const inMDNS, mDNSInterfaceData *inI
        
        // Tear down the multicast socket.
        
-       if ( inIFD->sock.fd != INVALID_SOCKET )
-       {
-               closesocket( inIFD->sock.fd );
-               inIFD->sock.fd = INVALID_SOCKET;
-       }
-       
+       UDPCloseSocket( &inIFD->sock );
+
        // If the interface is still referenced by items in the mDNS cache then put it on the inactive list. This keeps 
        // the InterfaceID valid so remove events report the correct interface. If it is no longer referenced, free it.
 
@@ -3184,22 +3136,25 @@ mDNSlocal mStatus       SockAddrToMDNSAddr( const struct sockaddr * const inSA, mDNSAd
 
 mDNSlocal OSStatus UDPBeginRecv( UDPSocket * sock )
 {
-       DWORD           size;
-       DWORD           numTries;
-       OSStatus        err;
-
-       require_action( socket != NULL, exit, err = kUnknownErr );
+       DWORD   size;
+       DWORD   numTries;
+       mStatus err;
 
+       dlog( kDebugLevelChatty, DEBUG_NAME "%s: sock = %d\n", __ROUTINE__, sock->fd );
+       
+       require_action( sock != NULL, exit, err = mStatus_BadStateErr );
+       check( !sock->overlapped.pending );
+       
        // Initialize the buffer structure
 
-       sock->wbuf.buf          = (char *) &sock->packet;
-       sock->wbuf.len          = (u_long) sizeof( sock->packet );
-       sock->srcAddrLen        = sizeof( sock->srcAddr );
+       sock->overlapped.wbuf.buf       = (char *) &sock->packet;
+       sock->overlapped.wbuf.len       = (u_long) sizeof( sock->packet );
+       sock->srcAddrLen                        = sizeof( sock->srcAddr );
 
        // Initialize the overlapped structure
 
-       ZeroMemory( &sock->overlapped, sizeof( OVERLAPPED ) );
-       sock->overlapped.hEvent = sock;
+       ZeroMemory( &sock->overlapped.data, sizeof( OVERLAPPED ) );
+       sock->overlapped.data.hEvent = sock;
 
        numTries = 0;
 
@@ -3209,13 +3164,13 @@ mDNSlocal OSStatus UDPBeginRecv( UDPSocket * sock )
                {
                        sock->wmsg.name                         = ( LPSOCKADDR ) &sock->srcAddr;
                        sock->wmsg.namelen                      = sock->srcAddrLen;
-                       sock->wmsg.lpBuffers            = &sock->wbuf;
+                       sock->wmsg.lpBuffers            = &sock->overlapped.wbuf;
                        sock->wmsg.dwBufferCount        = 1;
                        sock->wmsg.Control.buf          = ( CHAR* ) sock->controlBuffer;
                        sock->wmsg.Control.len          = sizeof( sock->controlBuffer );
                        sock->wmsg.dwFlags                      = 0;
 
-                       err = sock->recvMsgPtr( sock->fd, &sock->wmsg, &size, &sock->overlapped, ( LPWSAOVERLAPPED_COMPLETION_ROUTINE ) UDPEndRecv );
+                       err = sock->recvMsgPtr( sock->fd, &sock->wmsg, &size, &sock->overlapped.data, ( LPWSAOVERLAPPED_COMPLETION_ROUTINE ) UDPEndRecv );
                        err = translate_errno( ( err == 0 ) || ( WSAGetLastError() == WSA_IO_PENDING ), (OSStatus) WSAGetLastError(), kUnknownErr ); 
 
                        // <rdar://problem/7824093> iTunes 9.1 fails to install with Bonjour service on Windows 7 Ultimate
@@ -3231,7 +3186,7 @@ mDNSlocal OSStatus UDPBeginRecv( UDPSocket * sock )
                {
                        DWORD flags = 0;
 
-                       err = WSARecvFrom( sock->fd, &sock->wbuf, 1, NULL, &flags, ( LPSOCKADDR ) &sock->srcAddr, &sock->srcAddrLen, &sock->overlapped, ( LPWSAOVERLAPPED_COMPLETION_ROUTINE ) UDPEndRecv );
+                       err = WSARecvFrom( sock->fd, &sock->overlapped.wbuf, 1, NULL, &flags, ( LPSOCKADDR ) &sock->srcAddr, &sock->srcAddrLen, &sock->overlapped.data, ( LPWSAOVERLAPPED_COMPLETION_ROUTINE ) UDPEndRecv );
                        err = translate_errno( ( err == 0 ) || ( WSAGetLastError() == WSA_IO_PENDING ), ( OSStatus ) WSAGetLastError(), kUnknownErr );
                }
 
@@ -3248,6 +3203,8 @@ mDNSlocal OSStatus UDPBeginRecv( UDPSocket * sock )
        }
        while ( ( ( err == WSAECONNRESET ) || ( err == WSAEFAULT ) ) && ( numTries++ < 100 ) );
 
+       sock->overlapped.pending = TRUE;
+
 exit:
 
        if ( err )
@@ -3268,179 +3225,117 @@ mDNSlocal void CALLBACK UDPEndRecv( DWORD err, DWORD bytesTransferred, LPWSAOVER
        UDPSocket * sock = NULL;
 
        ( void ) flags;
-
+       
+       dlog( kDebugLevelChatty, DEBUG_NAME "%s: err = %d, bytesTransferred = %d\n", __ROUTINE__, err, bytesTransferred );
        require_action_quiet( err != WSA_OPERATION_ABORTED, exit, err = ( DWORD ) kUnknownErr );
        require_noerr( err, exit );
        sock = ( overlapped != NULL ) ? overlapped->hEvent : NULL;
        require_action( sock != NULL, exit, err = ( DWORD ) kUnknownErr );
-
-       // If we've closed the socket, then we want to ignore
-       // this read.  The packet might have been queued before
-       // the socket was closed.
-
-       if ( sock->fd != INVALID_SOCKET )
-       {
-               const mDNSInterfaceID   iid = sock->ifd ? sock->ifd->interfaceInfo.InterfaceID : NULL;
-               mDNSAddr                                srcAddr;
-               mDNSIPPort                              srcPort;
-               mDNSAddr                                dstAddr;
-               mDNSIPPort                              dstPort;
-               mDNSu8                          *       end;
+       dlog( kDebugLevelChatty, DEBUG_NAME "%s: sock = %d\n", __ROUTINE__, sock->fd );
+       sock->overlapped.error                          = err;
+       sock->overlapped.bytesTransferred       = bytesTransferred;
+       check( sock->overlapped.pending );
+       sock->overlapped.pending                        = FALSE;
        
-               // Translate the source of this packet into mDNS data types
+       // Translate the source of this packet into mDNS data types
 
-               SockAddrToMDNSAddr( (struct sockaddr *) &sock->srcAddr, &srcAddr, &srcPort );
+       SockAddrToMDNSAddr( (struct sockaddr *) &sock->srcAddr, &sock->overlapped.srcAddr, &sock->overlapped.srcPort );
        
-               // Initialize the destination of this packet. Just in case
-               // we can't determine this info because we couldn't call
-               // WSARecvMsg (recvMsgPtr)
+       // Initialize the destination of this packet. Just in case
+       // we can't determine this info because we couldn't call
+       // WSARecvMsg (recvMsgPtr)
 
-               dstAddr = sock->addr;
-               dstPort = sock->port;
+       sock->overlapped.dstAddr = sock->addr;
+       sock->overlapped.dstPort = sock->port;
 
-               if ( sock->recvMsgPtr )
-               {
-                       LPWSACMSGHDR    header;
-                       LPWSACMSGHDR    last = NULL;
-                       int                             count = 0;
+       if ( sock->recvMsgPtr )
+       {
+               LPWSACMSGHDR    header;
+               LPWSACMSGHDR    last = NULL;
+               int                             count = 0;
                
-                       // Parse the control information. Reject packets received on the wrong interface.
+               // Parse the control information. Reject packets received on the wrong interface.
                
-                       // <rdar://problem/7832196> INSTALL: Bonjour 2.0 on Windows can not start / stop
-                       // 
-                       // There seems to be an interaction between Bullguard and this next bit of code.
-                       // When a user's machine is running Bullguard, the control information that is
-                       // returned is corrupted, and the code would go into an infinite loop. We'll add
-                       // two bits of defensive coding here. The first will check that each pointer to
-                       // the LPWSACMSGHDR that is returned in the for loop is different than the last.
-                       // This fixes the problem with Bullguard. The second will break out of this loop
-                       // after 100 iterations, just in case the corruption isn't caught by the first
-                       // check.
-
-                       for( header = WSA_CMSG_FIRSTHDR( &sock->wmsg ); header; header = WSA_CMSG_NXTHDR( &sock->wmsg, header ) )
+               // <rdar://problem/7832196> INSTALL: Bonjour 2.0 on Windows can not start / stop
+               // 
+               // There seems to be an interaction between Bullguard and this next bit of code.
+               // When a user's machine is running Bullguard, the control information that is
+               // returned is corrupted, and the code would go into an infinite loop. We'll add
+               // two bits of defensive coding here. The first will check that each pointer to
+               // the LPWSACMSGHDR that is returned in the for loop is different than the last.
+               // This fixes the problem with Bullguard. The second will break out of this loop
+               // after 100 iterations, just in case the corruption isn't caught by the first
+               // check.
+
+               for ( header = WSA_CMSG_FIRSTHDR( &sock->wmsg ); header; header = WSA_CMSG_NXTHDR( &sock->wmsg, header ) )
+               {
+                       if ( ( header != last ) && ( ++count < 100 ) )
                        {
-                               if ( ( header != last ) && ( ++count < 100 ) )
-                               {
-                                       last = header;
+                               last = header;
                                        
-                                       if( ( header->cmsg_level == IPPROTO_IP ) && ( header->cmsg_type == IP_PKTINFO ) )
-                                       {
-                                               IN_PKTINFO * ipv4PacketInfo;
+                               if ( ( header->cmsg_level == IPPROTO_IP ) && ( header->cmsg_type == IP_PKTINFO ) )
+                               {
+                                       IN_PKTINFO * ipv4PacketInfo;
                                        
-                                               ipv4PacketInfo = (IN_PKTINFO *) WSA_CMSG_DATA( header );
-
-                                               if ( sock->ifd != NULL )
-                                               {
-                                                       require_action( ipv4PacketInfo->ipi_ifindex == sock->ifd->index, exit, err = ( DWORD ) kMismatchErr );
-                                               }
+                                       ipv4PacketInfo = (IN_PKTINFO *) WSA_CMSG_DATA( header );
 
-                                               dstAddr.type                            = mDNSAddrType_IPv4;
-                                               dstAddr.ip.v4.NotAnInteger      = ipv4PacketInfo->ipi_addr.s_addr;
-                                       }
-                                       else if( ( header->cmsg_level == IPPROTO_IPV6 ) && ( header->cmsg_type == IPV6_PKTINFO ) )
+                                       if ( sock->ifd != NULL )
                                        {
-                                               IN6_PKTINFO * ipv6PacketInfo;
-                                               
-                                               ipv6PacketInfo = (IN6_PKTINFO *) WSA_CMSG_DATA( header );
-               
-                                               if ( sock->ifd != NULL )
-                                               {
-                                                       require_action( ipv6PacketInfo->ipi6_ifindex == ( sock->ifd->index - kIPv6IfIndexBase ), exit, err = ( DWORD ) kMismatchErr );
-                                               }
-
-                                               dstAddr.type    = mDNSAddrType_IPv6;
-                                               dstAddr.ip.v6   = *( (mDNSv6Addr *) &ipv6PacketInfo->ipi6_addr );
+                                               require_action( ipv4PacketInfo->ipi_ifindex == sock->ifd->index, exit, err = ( DWORD ) kMismatchErr );
                                        }
+
+                                       sock->overlapped.dstAddr.type                           = mDNSAddrType_IPv4;
+                                       sock->overlapped.dstAddr.ip.v4.NotAnInteger     = ipv4PacketInfo->ipi_addr.s_addr;
                                }
-                               else
+                               else if( ( header->cmsg_level == IPPROTO_IPV6 ) && ( header->cmsg_type == IPV6_PKTINFO ) )
                                {
-                                       static BOOL loggedMessage = FALSE;
-
-                                       if ( !loggedMessage )
+                                       IN6_PKTINFO * ipv6PacketInfo;
+                                               
+                                       ipv6PacketInfo = (IN6_PKTINFO *) WSA_CMSG_DATA( header );
+               
+                                       if ( sock->ifd != NULL )
                                        {
-                                               LogMsg( "UDPEndRecv: WSARecvMsg control information error." );
-                                               loggedMessage = TRUE;
+                                               require_action( ipv6PacketInfo->ipi6_ifindex == ( sock->ifd->index - kIPv6IfIndexBase ), exit, err = ( DWORD ) kMismatchErr );
                                        }
 
-                                       break;
+                                       sock->overlapped.dstAddr.type   = mDNSAddrType_IPv6;
+                                       sock->overlapped.dstAddr.ip.v6  = *( (mDNSv6Addr *) &ipv6PacketInfo->ipi6_addr );
                                }
                        }
-               }
+                       else
+                       {
+                               static BOOL loggedMessage = FALSE;
 
-               // Dispatch the packet to mDNS.
-       
-               dlog( kDebugLevelChatty, DEBUG_NAME "packet received\n" );
-               dlog( kDebugLevelChatty, DEBUG_NAME "    size      = %d\n", bytesTransferred );
-               dlog( kDebugLevelChatty, DEBUG_NAME "    src       = %#a:%u\n", &srcAddr, ntohs( srcPort.NotAnInteger ) );
-               dlog( kDebugLevelChatty, DEBUG_NAME "    dst       = %#a:%u\n", &dstAddr, ntohs( dstPort.NotAnInteger ) );
-       
-               if ( sock->ifd != NULL )
-               {
-                       dlog( kDebugLevelChatty, DEBUG_NAME "    interface = %#a (index=0x%08X)\n", &sock->ifd->interfaceInfo.ip, sock->ifd->index );
+                               if ( !loggedMessage )
+                               {
+                                       LogMsg( "UDPEndRecv: WSARecvMsg control information error." );
+                                       loggedMessage = TRUE;
+                               }
+
+                               break;
+                       }
                }
+       }
 
-               dlog( kDebugLevelChatty, DEBUG_NAME "\n" );
+       dlog( kDebugLevelChatty, DEBUG_NAME "packet received\n" );
+       dlog( kDebugLevelChatty, DEBUG_NAME "    size      = %d\n", bytesTransferred );
+       dlog( kDebugLevelChatty, DEBUG_NAME "    src       = %#a:%u\n", &sock->overlapped.srcAddr, ntohs( sock->overlapped.srcPort.NotAnInteger ) );
+       dlog( kDebugLevelChatty, DEBUG_NAME "    dst       = %#a:%u\n", &sock->overlapped.dstAddr, ntohs( sock->overlapped.dstPort.NotAnInteger ) );
        
-               end = ( (mDNSu8 *) &sock->packet ) + bytesTransferred;
+       if ( sock->ifd != NULL )
+       {
+               dlog( kDebugLevelChatty, DEBUG_NAME "    interface = %#a (index=0x%08X)\n", &sock->ifd->interfaceInfo.ip, sock->ifd->index );
+       }
 
-               // <rdar://problem/7532492> mDNSResponder gets locking errors on Windows
-               //
-               // There seems to be a bug in WinSock with respect to Alertable I/O. According
-               // to MSDN <http://msdn.microsoft.com/en-us/library/aa363772(VS.85).aspx>, Alertable I/O
-               // callbacks will only be invoked during the following calls (when the caller sets
-               // the appropriate flag):
-               //
-               // - SleepEx
-               // - WaitForSingleObjectEx
-               // - WaitForMultipleObjectsEx
-               // - SignalObjectAndWait
-               // - MsgWaitForMultipleObjectsEx
-               //
-               // However, we have seen callbacks be invoked during calls to bind() (and maybe others).
-               // To workaround this, we set the gSocketEventsEnabled flag to be TRUE only when it's okay
-               // to directly call mDNSCoreReceive.  Otherwise we queue the packet up and dispatch it later.
+       dlog( kDebugLevelChatty, DEBUG_NAME "\n" );
 
-               if ( gSocketEventsEnabled && !gSocketEvents.Head )
-               {
-                       // Turn off socket events before calling mDNSCoreReceive().
-                       // This will prevent reentrancy problems.
-                       //
-                       // Don't forget to reenable socket events afterwards.
-                       
-                       SetSocketEventsEnabled( sock->m, FALSE );
-                       mDNSCoreReceive( sock->m, &sock->packet, end, &srcAddr, srcPort, &dstAddr, dstPort, iid );
-                       SetSocketEventsEnabled( sock->m, TRUE );
-               }
-               else
-               {
-                       UDPSocketEvent * event;
-                       
-                       event = malloc( sizeof( UDPSocketEvent ) );
-                       require_action( event, exit, err = ( DWORD ) kNoMemoryErr );
-                       event->super.sock               = sock;
-                       event->super.handler    = &UDPSocketEventHandler;
-                       event->super.next               = NULL;
-                       event->iid                              = iid;
-                       memcpy( &event->packet, &sock->packet, sizeof( event->packet ) );
-                       event->end                              = ( (mDNSu8 *) &event->packet ) + bytesTransferred;
-                       event->srcAddr                  = srcAddr;
-                       event->dstAddr                  = dstAddr;
-                       event->srcPort                  = srcPort;
-                       event->dstPort                  = dstPort;
-
-                       AddToTail( &gSocketEvents, event );
-               }
-       }
+       // Queue this socket
+       
+       AddToTail( &gUDPDispatchableSockets, sock );
 
 exit:
 
-       // If the socket is still good, then start up another asynchronous read
-
-       if ( sock && ( sock->fd != INVALID_SOCKET ) )
-       {
-               err = UDPBeginRecv( sock );
-               check_noerr( err );
-       }
+       return;
 }
 
 
@@ -4571,6 +4466,25 @@ exit:
 }
 
 
+//===========================================================================================================================
+//     TCPCloseSocket
+//===========================================================================================================================
+
+mDNSlocal void
+TCPCloseSocket( TCPSocket * sock )
+{
+       dlog( kDebugLevelChatty, DEBUG_NAME "closing TCPSocket 0x%x:%d\n", sock, sock->fd );
+
+       RemoveFromList( &gTCPDispatchableSockets, sock );       
+
+       if ( sock->fd != INVALID_SOCKET )
+       {
+               closesocket( sock->fd );
+               sock->fd = INVALID_SOCKET;
+       }
+}
+
+
 //===========================================================================================================================
 //     TCPFreeSocket
 //===========================================================================================================================
@@ -4598,6 +4512,25 @@ TCPFreeSocket( TCPSocket *sock )
 }
 
 
+//===========================================================================================================================
+//  UDPCloseSocket
+//===========================================================================================================================
+
+mDNSlocal void
+UDPCloseSocket( UDPSocket * sock )
+{
+       dlog( kDebugLevelChatty, DEBUG_NAME "closing UDPSocket %d\n", sock->fd );
+
+       RemoveFromList( &gUDPDispatchableSockets, sock );
+
+       if ( sock->fd != INVALID_SOCKET )
+       {
+               closesocket( sock->fd );
+               sock->fd = INVALID_SOCKET;
+       }
+}
+
+
 //===========================================================================================================================
 //  UDPFreeSocket
 //===========================================================================================================================
@@ -4607,7 +4540,7 @@ UDPFreeSocket( UDPSocket * sock )
 {
     check( sock );
 
-       dlog( kDebugLevelChatty, DEBUG_NAME "freeing UDPSocket %d (%##a)\n", sock->fd, &sock->addr );
+       dlog( kDebugLevelChatty, DEBUG_NAME "freeing UDPSocket %d\n", sock->fd );
 
     if ( sock->fd != INVALID_SOCKET )
     {          
@@ -4811,7 +4744,7 @@ mDNSlocal void SetDomainSecret( mDNS * const m, const domainname * inDomain )
                        require_action( ptr, exit, err = mStatus_NoMemoryErr );
                }
 
-               err = mDNS_SetSecretForDomain(m, ptr, &domain, &key, outSecret, mDNSfalse );
+               err = mDNS_SetSecretForDomain(m, ptr, &domain, &key, outSecret, NULL );
                require_action( err != mStatus_BadParamErr, exit, if (!foundInList ) mDNSPlatformMemFree( ptr ) );
 
                debugf("Setting shared secret for zone %s with key %##s", outDomain, key.c);
@@ -5150,118 +5083,85 @@ exit:
 }
 
 
-void 
-SetSocketEventsEnabled( mDNS * const inMDNS, BOOL enabled )
+void
+DispatchSocketEvents( mDNS * const inMDNS )
 {
-       DEBUG_UNUSED( inMDNS );
+       UDPSocket * udpSock;
+       TCPSocket * tcpSock;
 
-       if ( enabled )
+       while ( ( udpSock = ( UDPSocket* ) gUDPDispatchableSockets.Head ) != NULL )
        {
-               int numEvents = 0;
-
-               check( !gSocketEventsEnabled );
-
-               // If we're enabled, then drain the queue right now.
-
-               while ( gSocketEvents.Head && ( numEvents < 100 ) )
-               {
-                       SocketEvent * event = ( SocketEvent* ) gSocketEvents.Head;
-                       RemoveFromList( &gSocketEvents, event );
-                       check( event->handler );
-
-                       // At this point we're going to call our event handler, and
-                       // gSocketEventsEnabled should be FALSE.  So if the callback
-                       // does anything that causes alertable I/O callbacks to be
-                       // invoked, we'll queue the packets instead of invoking core
-                       // callbacks reentrantly.
-                       //
-                       // One potential problem here is a pathological case of
-                       // continuing to queue packets during invocation of
-                       // the callback, and thus never exiting out of this loop.
-                       // This is entirely theoretical as we've never seen it happen,
-                       // but just to be overly cautious, we'll only process a maximum of
-                       // 100 events here just in case.
-
-                       event->handler( inMDNS, event );
-                       free( event );
-
-                       numEvents++;
-               }
+               dlog( kDebugLevelChatty, DEBUG_NAME "%s: calling DispatchUDPEvent on socket %d, error = %d, bytesTransferred = %d\n",
+                                                    __ROUTINE__, udpSock->fd, udpSock->overlapped.error, udpSock->overlapped.bytesTransferred );
+               RemoveFromList( &gUDPDispatchableSockets, udpSock );
+               DispatchUDPEvent( inMDNS, udpSock );
+       }
+               
+       while ( ( tcpSock = ( TCPSocket* ) gTCPDispatchableSockets.Head ) != NULL )
+       {
+               dlog( kDebugLevelChatty, DEBUG_NAME "%s: calling DispatchTCPEvent on socket %d, error = %d, bytesTransferred = %d\n",
+                                                    __ROUTINE__, tcpSock->fd, tcpSock->overlapped.error, tcpSock->overlapped.bytesTransferred );
+               RemoveFromList( &gTCPDispatchableSockets, tcpSock );
+               DispatchTCPEvent( inMDNS, tcpSock );
        }
-
-       gSocketEventsEnabled = enabled;
 }
 
 
 mDNSlocal void
-FreeSocketEventsForSocket( mDNS * const inMDNS, void * sock )
+DispatchUDPEvent( mDNS * const inMDNS, UDPSocket * sock )
 {
-       SocketEvent * event;
+       ( void ) inMDNS;
 
-       DEBUG_UNUSED( inMDNS );
+       // If we've closed the socket, then we want to ignore
+       // this read.  The packet might have been queued before
+       // the socket was closed.
 
-       for ( event = ( SocketEvent* ) gSocketEvents.Head; event; event = event->next )
+       if ( sock->fd != INVALID_SOCKET )
        {
-               if ( event->sock == sock )
-               {
-                       RemoveFromList( &gSocketEvents, event );
-                       free( event );
-                       break;
-               }
-       }
-}
+               const mDNSInterfaceID   iid = sock->ifd ? sock->ifd->interfaceInfo.InterfaceID : NULL;
+               mDNSu8                          *       end = ( (mDNSu8 *) &sock->packet ) + sock->overlapped.bytesTransferred;
 
+               dlog( kDebugLevelChatty, DEBUG_NAME "calling mDNSCoreReceive on socket: %d\n", sock->fd );
+               mDNSCoreReceive( sock->m, &sock->packet, end, &sock->overlapped.srcAddr, sock->overlapped.srcPort, &sock->overlapped.dstAddr, sock->overlapped.dstPort, iid );
+       }
 
-mDNSlocal void
-FreeSocketEvents( mDNS * const inMDNS )
-{
-       DEBUG_UNUSED( inMDNS );
+       // If the socket is still good, then start up another asynchronous read
 
-       while ( gSocketEvents.Head )
+       if ( sock->fd != INVALID_SOCKET )
        {
-               SocketEvent * event = ( SocketEvent* ) gSocketEvents.Head;
-               RemoveFromList( &gSocketEvents, event );
-               free( event );
+               int err = UDPBeginRecv( sock );
+               check_noerr( err );
        }
 }
 
 
 mDNSlocal void
-TCPSocketEventHandler( mDNS * const inMDNS, void * v )
+DispatchTCPEvent( mDNS * const inMDNS, TCPSocket * sock )
 {
-       TCPSocketEvent  * event = ( TCPSocketEvent* ) v;
-       TCPSocket               * sock = ( TCPSocket* ) event->super.sock;
-       
-       DEBUG_UNUSED( inMDNS );
-       check( sock );
-
-       sock->lastError = event->error;
+       ( void ) inMDNS;
 
-       if ( !event->error )
+       if ( sock->fd != INVALID_SOCKET )
        {
-               if ( event->bytesTransferred )
+               sock->eptr += sock->overlapped.bytesTransferred;
+               sock->lastError = sock->overlapped.error;
+
+               if ( !sock->overlapped.error && !sock->overlapped.bytesTransferred )
                {
-                       memcpy( sock->buf, event->buf, event->bytesTransferred );
-                       sock->bptr = sock->buf;
-                       sock->eptr = sock->buf + event->bytesTransferred;
+                       sock->closed = TRUE;
                }
-               else
+
+               if ( sock->readEventHandler != NULL )
                {
-                       sock->closed = TRUE;
+                       dlog( kDebugLevelChatty, DEBUG_NAME "calling TCP read handler  on socket: %d\n", sock->fd );
+                       sock->readEventHandler( sock );
                }
        }
 
-       if ( sock->readEventHandler != NULL )
+       // If the socket is still good, then start up another asynchronous read
+
+       if ( !sock->closed && ( sock->fd != INVALID_SOCKET ) )
        {
-               sock->readEventHandler( sock );
+               int err = TCPBeginRecv( sock );
+               check_noerr( err );
        }
 }
-
-
-mDNSlocal void
-UDPSocketEventHandler( mDNS * const inMDNS, void * v )
-{
-       UDPSocketEvent * event = ( UDPSocketEvent* ) v;
-
-       mDNSCoreReceive( inMDNS, &event->packet, event->end, &event->srcAddr, event->srcPort, &event->dstAddr, event->dstPort, event->iid );
-}              
index 95e1fddfe6ff1a7fa30a9b26d02ea5ee2d8accbe..e08c462b637de7c37e1cbd94d238baf006c6b1a6 100755 (executable)
        extern "C" {
 #endif
 
+
+typedef struct Overlapped
+{
+       BOOL            pending;
+       OVERLAPPED      data;
+       WSABUF          wbuf;
+       DWORD           error;
+       DWORD           bytesTransferred;
+       mDNSAddr        srcAddr;
+       mDNSIPPort      srcPort;
+       mDNSAddr        dstAddr;
+       mDNSIPPort      dstPort;
+} Overlapped;
+
+
 typedef void ( *TCPReadEventHandler )( TCPSocket * sock );
 typedef void ( *TCPUserCallback )();
 
@@ -43,13 +58,14 @@ struct TCPSocket_struct
        BOOL                                            connected;
        TCPUserCallback                         userCallback;
        void                                    *       userContext;
+       Overlapped                                      overlapped;
        DWORD                                           lastError;
        BOOL                                            closed;\r
-       OVERLAPPED                                      overlapped;
-       WSABUF                                          wbuf;
-       uint8_t                                         buf[ 4192 ];
+       uint8_t                                         bbuf[ 4192 ];
        uint8_t                                 *       bptr;
        uint8_t                                 *       eptr;
+       uint8_t                                 *       ebuf;
+       TCPSocket                               *       nextDispatchable;
        mDNS                                    *       m;
 };
 
@@ -61,53 +77,19 @@ struct UDPSocket_struct
                                                                                                        // dstAddr from WSARecvMsg we use this value instead.
        SOCKET                                                  fd;
        LPFN_WSARECVMSG                                 recvMsgPtr;
-       OVERLAPPED                                              overlapped;
+       Overlapped                                              overlapped;
        WSAMSG                                                  wmsg;
-       WSABUF                                                  wbuf;
        DNSMessage                                              packet;
        uint8_t                                                 controlBuffer[ 128 ];
        struct sockaddr_storage                 srcAddr;                // This is filled in by the WSARecv* function
        INT                                                             srcAddrLen;             // See above
        struct mDNSInterfaceData        *       ifd;
+       UDPSocket                                       *       nextDispatchable;
        UDPSocket                                       *       next;
        mDNS                                            *       m;
 };
 
 
-typedef void ( *SocketEventHandler )( mDNS * const inMDNS, void * v );
-
-
-typedef struct SocketEvent
-{
-       void                                    *       sock;
-       SocketEventHandler                      handler;
-       struct SocketEvent              *       next;
-} SocketEvent;
-
-
-typedef struct TCPSocketEvent
-{
-       struct SocketEvent                      super;
-       DWORD                                           error;
-       DWORD                                           bytesTransferred;
-       uint8_t                                         buf[ 4192 ];
-} TCPSocketEvent;
-
-
-typedef struct UDPSocketEvent
-{
-       struct SocketEvent                      super;
-       mDNSInterfaceID                         iid;
-       DNSMessage                                      packet;
-       mDNSu8                                  *       end;
-       mDNSAddr                                        srcAddr;
-       mDNSIPPort                                      srcPort;
-       mDNSAddr                                        dstAddr;
-       mDNSIPPort                                      dstPort;
-} UDPSocketEvent;
-
-
-
 //---------------------------------------------------------------------------------------------------------------------------
 /*!    @struct         mDNSInterfaceData
 
@@ -216,7 +198,7 @@ extern mStatus  TCPAddSocket( mDNS * const inMDNS, TCPSocket *sock );
 extern mStatus SetupInterfaceList( mDNS * const inMDNS );
 extern mStatus TearDownInterfaceList( mDNS * const inMDNS );
 extern BOOL            IsWOMPEnabled();
-extern void            SetSocketEventsEnabled( mDNS * const inMDNS, BOOL val );
+extern void     DispatchSocketEvents( mDNS * const inMDNS );
 
 
 #ifdef __cplusplus