]> git.saurik.com Git - apple/mdnsresponder.git/commitdiff
mDNSResponder-522.1.11.tar.gz os-x-109 os-x-1091 v522.1.11
authorApple <opensource@apple.com>
Tue, 29 Oct 2013 14:59:42 +0000 (14:59 +0000)
committerApple <opensource@apple.com>
Tue, 29 Oct 2013 14:59:42 +0000 (14:59 +0000)
78 files changed:
Clients/dns-sd.c
Clients/dnsctl.c [new file with mode: 0644]
Clients/mDNSNetMonitor.VisualStudio/mDNSNetMonitor.vcxproj
Clients/mDNSNetMonitor.VisualStudio/mDNSNetMonitor.vcxproj.filters
Makefile
mDNSCore/CryptoAlg.c
mDNSCore/CryptoAlg.h
mDNSCore/DNSCommon.c
mDNSCore/DNSCommon.h
mDNSCore/DNSDigest.c
mDNSCore/anonymous.c [new file with mode: 0644]
mDNSCore/anonymous.h [new file with mode: 0644]
mDNSCore/dnsproxy.c [new file with mode: 0644]
mDNSCore/dnsproxy.h [new file with mode: 0644]
mDNSCore/dnssec.c
mDNSCore/dnssec.h
mDNSCore/mDNS.c
mDNSCore/mDNSDebug.h
mDNSCore/mDNSEmbeddedAPI.h
mDNSCore/nsec.c
mDNSCore/nsec.h
mDNSCore/nsec3.c [new file with mode: 0644]
mDNSCore/nsec3.h [new file with mode: 0644]
mDNSCore/uDNS.c
mDNSCore/uDNS.h
mDNSMacOSX/BonjourEvents.c
mDNSMacOSX/CUPolicy.c [new file with mode: 0644]
mDNSMacOSX/CryptoSupport.c
mDNSMacOSX/DNSProxySupport.c [new file with mode: 0644]
mDNSMacOSX/DNSSECSupport.c [new file with mode: 0644]
mDNSMacOSX/DNSSECSupport.h [new file with mode: 0644]
mDNSMacOSX/DNSServiceDiscovery.c
mDNSMacOSX/LaunchDaemonInfo.helper.plist
mDNSMacOSX/LaunchDaemonInfo.plist
mDNSMacOSX/LegacyNATTraversal.c
mDNSMacOSX/Private/dns_services.c [new file with mode: 0644]
mDNSMacOSX/Private/dns_services.h [new file with mode: 0644]
mDNSMacOSX/Private/dns_xpc.h [new file with mode: 0644]
mDNSMacOSX/Private/xpc_services.c [new file with mode: 0644]
mDNSMacOSX/Private/xpc_services.h [new file with mode: 0644]
mDNSMacOSX/README.privsep
mDNSMacOSX/VPNService.c [new file with mode: 0644]
mDNSMacOSX/com.apple.networking.mDNSResponder [new file with mode: 0644]
mDNSMacOSX/daemon.c
mDNSMacOSX/dnsctl-entitlements.plist [new file with mode: 0644]
mDNSMacOSX/helper-error.h
mDNSMacOSX/helper-main.c
mDNSMacOSX/helper-stubs.c
mDNSMacOSX/helper.c
mDNSMacOSX/helper.h
mDNSMacOSX/helpermsg.defs
mDNSMacOSX/mDNSMacOSX.c
mDNSMacOSX/mDNSMacOSX.h
mDNSMacOSX/mDNSResponder-entitlements.plist
mDNSMacOSX/mDNSResponder.sb
mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj
mDNSMacOSX/mDNSResponderLogging.mobileconfig [new file with mode: 0644]
mDNSPosix/Client.c
mDNSPosix/Identify.c
mDNSPosix/Makefile
mDNSPosix/NetMonitor.c
mDNSPosix/mDNSPosix.c
mDNSShared/PlatformCommon.c
mDNSShared/dns_sd.h
mDNSShared/dnsextd.c
mDNSShared/dnssd_clientshim.c
mDNSShared/dnssd_clientstub.c
mDNSShared/dnssd_ipc.h
mDNSShared/mDNSDebug.c
mDNSShared/uds_daemon.c
mDNSShared/uds_daemon.h
mDNSWindows/DLLX/DLLX.idl
mDNSWindows/DLLX/DLLX.vcxproj
mDNSWindows/Secret.c
mDNSWindows/Secret.h
mDNSWindows/SystemService/Service.vcxproj
mDNSWindows/SystemService/Service.vcxproj.filters
mDNSWindows/mDNSWin32.c

index 146562f1a31211557185326dc533231196826c47..8d5e75febad3beb5da32f5ec950cfaae0799dbb6 100644 (file)
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 4 -*-
  *
 /* -*- Mode: C; tab-width: 4 -*-
  *
- * Copyright (c) 2002-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2002-2013 Apple Inc. All rights reserved.
  *
  * Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
  * ("Apple") in consideration of your agreement to the following terms, and your
  *
  * Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
  * ("Apple") in consideration of your agreement to the following terms, and your
@@ -181,6 +181,10 @@ static const char kFilePathSep = '/';
 #include "../mDNSShared/dnssd_clientstub.c"
 #endif
 
 #include "../mDNSShared/dnssd_clientstub.c"
 #endif
 
+#if _DNS_SD_LIBDISPATCH
+#include <dispatch/private.h>
+#endif
+
 // The "+0" is to cope with the case where _DNS_SD_H is defined but empty (e.g. on Mac OS X 10.4 and earlier)
 #if _DNS_SD_H+0 >= 116
 #define HAS_NAT_PMP_API 1
 // The "+0" is to cope with the case where _DNS_SD_H is defined but empty (e.g. on Mac OS X 10.4 and earlier)
 #if _DNS_SD_H+0 >= 116
 #define HAS_NAT_PMP_API 1
@@ -192,6 +196,41 @@ static const char kFilePathSep = '/';
 //*************************************************************************************************************
 // Globals
 
 //*************************************************************************************************************
 // Globals
 
+#define DS_FIXED_SIZE   4
+typedef struct
+{
+    unsigned short keyTag;
+    unsigned char alg; 
+    unsigned char digestType;
+    unsigned char  *digest;
+} rdataDS;
+
+#define DNSKEY_FIXED_SIZE    4
+typedef struct
+{
+    unsigned short flags;
+    unsigned char proto;
+    unsigned char alg;
+    unsigned char *data;
+} rdataDNSKey;
+
+//size of rdataRRSIG excluding signerName and signature (which are variable fields) 
+#define RRSIG_FIXED_SIZE      18
+typedef struct
+{
+    unsigned short typeCovered;
+    unsigned char alg; 
+    unsigned char labels;
+    unsigned int origTTL;
+    unsigned int sigExpireTime;
+    unsigned int sigInceptTime;
+    unsigned short keyTag;
+    char signerName[256];
+    //unsigned char *signature
+} rdataRRSig;
+
+#define RR_TYPE_SIZE 16
+
 typedef union { unsigned char b[2]; unsigned short NotAnInteger; } Opaque16;
 
 static int operation;
 typedef union { unsigned char b[2]; unsigned short NotAnInteger; } Opaque16;
 
 static int operation;
@@ -228,6 +267,13 @@ static volatile int timeOut = LONG_TIME;
 
 //*************************************************************************************************************
 // Supporting Utility Functions
 
 //*************************************************************************************************************
 // Supporting Utility Functions
+static uint16_t GetRRClass(const char *s)   
+{
+    if (!strcasecmp(s, "IN")) 
+        return kDNSServiceClass_IN;
+    else
+        return(atoi(s));
+}     
 
 static uint16_t GetRRType(const char *s)
 {
 
 static uint16_t GetRRType(const char *s)
 {
@@ -286,6 +332,95 @@ static uint16_t GetRRType(const char *s)
     else return(atoi(s));
 }
 
     else return(atoi(s));
 }
 
+static char *DNSTypeName(unsigned short rr_type)
+{
+    switch (rr_type)
+    {
+        case kDNSServiceType_A:         return("Addr");
+        case kDNSServiceType_NS:        return("NS");
+        case kDNSServiceType_MX:        return("MX");
+        case kDNSServiceType_CNAME:     return("CNAME");
+        case kDNSServiceType_SOA:       return("SOA");
+        case kDNSServiceType_PTR:       return("PTR");
+        case kDNSServiceType_AAAA:      return("AAAA");
+        case kDNSServiceType_NSEC:      return("NSEC");
+        case kDNSServiceType_TSIG:      return("TSIG");
+        case kDNSServiceType_RRSIG:     return("RRSIG");
+        case kDNSServiceType_DNSKEY:    return("DNSKEY");
+        case kDNSServiceType_DS:        return("DS");
+        default:            
+        {
+            static char buffer[RR_TYPE_SIZE];
+            snprintf(buffer, sizeof(buffer), "TYPE%d", rr_type);
+            return(buffer);
+        }
+    }
+}
+
+static unsigned short swap16(unsigned short x)
+{
+    unsigned char *ptr = (unsigned char *)&x;
+    return (unsigned short)((unsigned short)ptr[0] << 8 | ptr[1]);
+}
+
+static unsigned int swap32(unsigned int x) 
+{
+    unsigned char *ptr = (unsigned char *)&x;
+    return (unsigned int)((unsigned int)ptr[0] << 24 | (unsigned int)ptr[1] << 16 | (unsigned int)ptr[2] << 8 | ptr[3]);
+}
+static unsigned int keytag(unsigned char *key, unsigned int keysize)  
+{
+    unsigned long ac;
+    unsigned int i;
+
+    for (ac = 0, i = 0; i < keysize; ++i)
+        ac += (i & 1) ? key[i] : key[i] << 8;
+    ac += (ac >> 16) & 0xFFFF;
+    return ac & 0xFFFF;
+}
+
+static void base64Encode(char *buffer, int buflen, void *rdata, unsigned int rdlen)
+{
+#if _DNS_SD_LIBDISPATCH
+    const void *result = NULL;
+    size_t size;
+    dispatch_data_t src_data = NULL, dest_data = NULL, null_str = NULL, data = NULL, map = NULL;
+
+    src_data = dispatch_data_create(rdata, rdlen, dispatch_get_global_queue(0, 0), ^{});
+    if (!src_data)
+        goto done;
+
+    dest_data = dispatch_data_create_with_transform(src_data, DISPATCH_DATA_FORMAT_TYPE_NONE, DISPATCH_DATA_FORMAT_TYPE_BASE64);
+    if (!dest_data)
+        goto done;
+
+    null_str = dispatch_data_create("", 1, dispatch_get_global_queue(0, 0), ^{});
+    if (!null_str)
+        goto done;
+    data = dispatch_data_create_concat(dest_data, null_str);
+    if (!data)
+        goto done;    
+
+    map = dispatch_data_create_map(data, &result, &size);    
+    if (!map)
+        goto done;
+
+    snprintf(buffer, buflen, " %s", (char *)result);
+    
+done:
+    if (src_data) dispatch_release(src_data);
+    if (dest_data) dispatch_release(dest_data);
+    if (data)     dispatch_release(data);
+    if (null_str) dispatch_release(null_str);
+    if (map)      dispatch_release(map);
+    return;
+#else  //_DNS_SD_LIBDISPATCH
+    snprintf(buffer, buflen, " %s", ".");
+    return;
+#endif //_DNS_SD_LIBDISPATCH 
+}
+
 #if HAS_NAT_PMP_API | HAS_ADDRINFO_API
 static DNSServiceProtocol GetProtocol(const char *s)
 {
 #if HAS_NAT_PMP_API | HAS_ADDRINFO_API
 static DNSServiceProtocol GetProtocol(const char *s)
 {
@@ -301,6 +436,7 @@ static DNSServiceProtocol GetProtocol(const char *s)
 }
 #endif
 
 }
 #endif
 
+
 //*************************************************************************************************************
 // Sample callback functions for each of the operation types
 
 //*************************************************************************************************************
 // Sample callback functions for each of the operation types
 
@@ -331,40 +467,63 @@ static void printtimestamp(void)
     printf("%2d:%02d:%02d.%03d  ", tm.tm_hour, tm.tm_min, tm.tm_sec, ms);
 }
 
     printf("%2d:%02d:%02d.%03d  ", tm.tm_hour, tm.tm_min, tm.tm_sec, ms);
 }
 
+// formating time to RFC 4034 format
+static void FormatTime(unsigned long te, unsigned char *buf, int bufsize) 
+{
+    struct tm tmTime;
+#ifdef _WIN32
+       __time32_t t = (__time32_t) te;
+       _gmtime32_s(&tmTime, &t);
+#else
+    // Time since epoch : strftime takes "tm". Convert seconds to "tm" using
+    // gmtime_r first and then use strftime
+       time_t t = (time_t)te;
+       gmtime_r(&t, &tmTime);
+#endif
+    strftime((char *)buf, bufsize, "%Y%m%d%H%M%S", &tmTime);
+}
+
 static void print_usage(const char *arg0, int print_all)
 {
 static void print_usage(const char *arg0, int print_all)
 {
-    fprintf(stderr, "%s -E                  (Enumerate recommended registration domains)\n", arg0);
-    fprintf(stderr, "%s -F                      (Enumerate recommended browsing domains)\n", arg0);
-    fprintf(stderr, "%s -R <Name> <Type> <Domain> <Port> [<TXT>...] (Register a service)\n", arg0);
-    fprintf(stderr, "%s -B        <Type> <Domain>        (Browse for services instances)\n", arg0);
-    fprintf(stderr, "%s -L <Name> <Type> <Domain>           (Look up a service instance)\n", arg0);
-    fprintf(stderr, "%s -P <Name> <Type> <Domain> <Port> <Host> <IP> [<TXT>...]  (Proxy)\n", arg0);
-    fprintf(stderr, "%s -q <name> <rrtype> <rrclass> (Generic query for any record type)\n", arg0);
-    fprintf(stderr, "%s -Z        <Type> <Domain>   (Output results in Zone File format)\n", arg0);
+    fprintf(stderr, "%s -E                              (Enumerate recommended registration domains)\n", arg0);
+    fprintf(stderr, "%s -F                                  (Enumerate recommended browsing domains)\n", arg0);
+    fprintf(stderr, "%s -R <Name> <Type> <Domain> <Port> [<TXT>...]             (Register a service)\n", arg0);
+    fprintf(stderr, "%s -B        <Type> <Domain>                    (Browse for services instances)\n", arg0);
+    fprintf(stderr, "%s -L <Name> <Type> <Domain>                       (Look up a service instance)\n", arg0);
+    fprintf(stderr, "%s -P <Name> <Type> <Domain> <Port> <Host> <IP> [<TXT>...]              (Proxy)\n", arg0);
+    fprintf(stderr, "%s -q <name> <rrtype> <rrclass>             (Generic query for any record type)\n", arg0);
+    fprintf(stderr, "%s -D <name> <rrtype> <rrclass>(Validate query for any record type with DNSSEC)\n", arg0);
+    fprintf(stderr, "%s -Z        <Type> <Domain>               (Output results in Zone File format)\n", arg0);
 #if HAS_ADDRINFO_API
 #if HAS_ADDRINFO_API
-    fprintf(stderr, "%s -G     v4/v6/v4v6 <name>  (Get address information for hostname)\n", arg0);
+    fprintf(stderr, "%s -G     v4/v6/v4v6 <name>              (Get address information for hostname)\n", arg0);
+    fprintf(stderr, "%s -g v4/v6/v4v6 <name>        (Validate address info for hostname with DNSSEC)\n", arg0);
 #endif
 #endif
-    fprintf(stderr, "%s -V    (Get version of currently running daemon / system service)\n", arg0);
+    fprintf(stderr, "%s -V                (Get version of currently running daemon / system service)\n", arg0);
 
     if (print_all)  //Print all available options for dns-sd tool
     {
 
     if (print_all)  //Print all available options for dns-sd tool
     {
-        fprintf(stderr, "%s -C <FQDN> <rrtype> <rrclass>   (Query; reconfirming each result)\n", arg0);
+        fprintf(stderr, "%s -C <FQDN> <rrtype> <rrclass>               (Query; reconfirming each result)\n", arg0);
 #if HAS_NAT_PMP_API
 #if HAS_NAT_PMP_API
-        fprintf(stderr, "%s -X udp/tcp/udptcp <IntPort> <ExtPort> <TTL>   (NAT Port Mapping)\n", arg0);
+        fprintf(stderr, "%s -X udp/tcp/udptcp <IntPort> <ExtPort> <TTL>               (NAT Port Mapping)\n", arg0);
 #endif
 #endif
-        fprintf(stderr, "%s -A                      (Test Adding/Updating/Deleting a record)\n", arg0);
-        fprintf(stderr, "%s -U                                  (Test updating a TXT record)\n", arg0);
-        fprintf(stderr, "%s -N                             (Test adding a large NULL record)\n", arg0);
-        fprintf(stderr, "%s -T                            (Test creating a large TXT record)\n", arg0);
-        fprintf(stderr, "%s -M      (Test creating a registration with multiple TXT records)\n", arg0);
-        fprintf(stderr, "%s -I   (Test registering and then immediately updating TXT record)\n", arg0);
-        fprintf(stderr, "%s -S                 (Test multiple operations on a shared socket)\n", arg0);
-        fprintf(stderr, "%s -i <Interface> (Run dns-sd cmd on a specific interface (en0/en1)\n", arg0);
-        fprintf(stderr, "%s -lo                  (Run dns-sd cmd using local only interface)\n", arg0);
-        fprintf(stderr, "%s -p2p                          (Use kDNSServiceInterfaceIndexP2P)\n", arg0);
-        fprintf(stderr, "%s -includep2p                (Set kDNSServiceFlagsIncludeP2P flag)\n", arg0);
-        fprintf(stderr, "%s -includeAWDL              (Set kDNSServiceFlagsIncludeAWDL flag)\n", arg0);
-        fprintf(stderr, "%s -tc            (Set kDNSServiceFlagsBackgroundTrafficClass flag)\n", arg0);
+        fprintf(stderr, "%s -A                                  (Test Adding/Updating/Deleting a record)\n", arg0);
+        fprintf(stderr, "%s -U                                              (Test updating a TXT record)\n", arg0);
+        fprintf(stderr, "%s -N                                         (Test adding a large NULL record)\n", arg0);
+        fprintf(stderr, "%s -T                                        (Test creating a large TXT record)\n", arg0);
+        fprintf(stderr, "%s -M                  (Test creating a registration with multiple TXT records)\n", arg0);
+        fprintf(stderr, "%s -I               (Test registering and then immediately updating TXT record)\n", arg0);
+        fprintf(stderr, "%s -S                             (Test multiple operations on a shared socket)\n", arg0);
+        fprintf(stderr, "%s -i <Interface>             (Run dns-sd cmd on a specific interface (en0/en1)\n", arg0);
+        fprintf(stderr, "%s -lo                              (Run dns-sd cmd using local only interface)\n", arg0);
+        fprintf(stderr, "%s -p2p                                      (Use kDNSServiceInterfaceIndexP2P)\n", arg0);
+        fprintf(stderr, "%s -includep2p                            (Set kDNSServiceFlagsIncludeP2P flag)\n", arg0);
+        fprintf(stderr, "%s -includeAWDL                          (Set kDNSServiceFlagsIncludeAWDL flag)\n", arg0);
+        fprintf(stderr, "%s -optional                        (Set kDNSServiceFlagsValidateOptional flag)\n", arg0);
+        fprintf(stderr, "%s -tc                        (Set kDNSServiceFlagsBackgroundTrafficClass flag)\n", arg0);
+        fprintf(stderr, "%s -unicastResponse                  (Set kDNSServiceFlagsUnicastResponse flag)\n", arg0);
+        fprintf(stderr, "%s -t1                                  (Set kDNSServiceFlagsThresholdOne flag)\n", arg0);
+        fprintf(stderr, "%s -tFinder                          (Set kDNSServiceFlagsThresholdFinder flag)\n", arg0);
+        fprintf(stderr, "%s -timeout                                  (Set kDNSServiceFlagsTimeout flag)\n", arg0);
     }
 }
 
     }
 }
 
@@ -535,10 +694,13 @@ static void DNSSD_API browse_reply(DNSServiceRef sdref, const DNSServiceFlags fl
     (void)context;      // Unused
     EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode);
 
     (void)context;      // Unused
     EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode);
 
-    if (num_printed++ == 0) printf("Timestamp     A/R Flags if %-25s %-25s %s\n", "Domain", "Service Type", "Instance Name");
+    if (num_printed++ == 0) printf("Timestamp     A/R    Flags  if %-20s %-20s %s\n", "Domain", "Service Type", "Instance Name");
     printtimestamp();
     printtimestamp();
-    if (errorCode) printf("Error code %d\n", errorCode);
-    else printf("%s%6X%3d %-25s %-25s %s\n", op, flags, ifIndex, replyDomain, replyType, replyName);
+    if (errorCode) 
+        printf("Error code %d\n", errorCode);
+    else 
+        printf("%s %8X %3d %-20s %-20s %s\n", 
+                op, flags, ifIndex, replyDomain, replyType, replyName);
     if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout);
 
     // To test selective cancellation of operations of shared sockets,
     if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout);
 
     // To test selective cancellation of operations of shared sockets,
@@ -707,78 +869,247 @@ static int snprintd(char *p, int max, const unsigned char **rd)
 {
     const char *const buf = p;
     const char *const end = p + max;
 {
     const char *const buf = p;
     const char *const end = p + max;
-    while (**rd) { p += snprintf(p, end-p, "%.*s.", **rd, *rd+1); *rd += 1 + **rd; }
+    while (**rd) 
+    { 
+        p += snprintf(p, end-p, "%.*s.", **rd, *rd+1); 
+        *rd += 1 + **rd; 
+    }
     *rd += 1;   // Advance over the final zero byte
     return(p-buf);
 }
 
     *rd += 1;   // Advance over the final zero byte
     return(p-buf);
 }
 
+static void ParseDNSSECRecords(uint16_t rrtype, char *rdb, char *p, unsigned const char *rd, uint16_t rdlen)
+{
+    int rdb_size = 1000;
+    switch (rrtype) 
+    {
+        case kDNSServiceType_DS:
+        {
+            unsigned char *ptr;
+            int i;
+            rdataDS *rrds = (rdataDS *)rd;
+            p += snprintf(p, rdb + rdb_size - p, "%d  %d  %d  ",
+                          rrds->alg, swap16(rrds->keyTag), rrds->digestType);
+            ptr = (unsigned char *)(rd + DS_FIXED_SIZE);
+            for (i = 0; i < (rdlen - DS_FIXED_SIZE); i++)
+                p += snprintf(p, rdb + rdb_size - p, "%x", ptr[i]);   
+            break; 
+        } 
+            
+        case kDNSServiceType_DNSKEY:
+        {
+            rdataDNSKey *rrkey = (rdataDNSKey *)rd;
+            p += snprintf(p, rdb + rdb_size - p, "%d  %d  %d  %u", swap16(rrkey->flags), rrkey->proto,
+                          rrkey->alg, (unsigned int)keytag((unsigned char *)rrkey, rdlen));
+            base64Encode(p, rdb + rdb_size - p, (unsigned char *)(rd + DNSKEY_FIXED_SIZE), rdlen - DNSKEY_FIXED_SIZE);
+            break;
+        }
+            
+        case kDNSServiceType_NSEC: 
+        {
+            unsigned char *next = (unsigned char *)rd;
+            int len, bitmaplen;
+            int win, wlen, type;
+            unsigned char *bmap;
+            char *l = NULL;
+            
+            l = p;
+            p += snprintd(p, rdb + rdb_size - p, &rd);
+            len = p - l + 1;
+            
+            bitmaplen = rdlen - len;
+            bmap = (unsigned char *)((unsigned char *)next + len);
+            
+            while (bitmaplen > 0)
+            {
+                int i;
+                
+                if (bitmaplen < 3)
+                {
+                    printf("Case NSEC: malformed nsec, bitmaplen %d short\n", bitmaplen);
+                    break;
+                }   
+                
+                win = *bmap++;
+                wlen = *bmap++;
+                bitmaplen -= 2;
+                if (bitmaplen < wlen || wlen < 1 || wlen > 32)
+                {
+                    printf("Case NSEC: malformed nsec, bitmaplen %d wlen %d\n", bitmaplen, wlen);
+                    break;
+                }
+                if (win < 0 || win >= 256)
+                {
+                    printf("Case NSEC: malformed nsec, bad window win %d\n", win);
+                    break;
+                }
+                type = win * 256;
+                for (i = 0; i < wlen * 8; i++)
+                {
+                    if (bmap[i>>3] & (128 >> (i&7)))
+                        p += snprintf(p, rdb + rdb_size - p, " %s ", DNSTypeName(type + i));
+                }
+                bmap += wlen;
+                bitmaplen -= wlen;
+            }
+            break;
+        }
+            
+        case kDNSServiceType_RRSIG:    
+        {
+            rdataRRSig *rrsig = (rdataRRSig *)rd;
+            unsigned char expTimeBuf[64];
+            unsigned char inceptTimeBuf[64];
+            unsigned long inceptClock;
+            unsigned long expClock;
+            const unsigned char *q = NULL;
+            char *k = NULL;
+            int len;
+            
+            expClock = (unsigned long)swap32(rrsig->sigExpireTime);
+            FormatTime(expClock, expTimeBuf, sizeof(expTimeBuf));
+            
+            inceptClock = (unsigned long)swap32(rrsig->sigInceptTime);
+            FormatTime(inceptClock, inceptTimeBuf, sizeof(inceptTimeBuf));
+            
+            p += snprintf(p, rdb + rdb_size - p, " %-7s  %d  %d  %d  %s  %s  %7d  ",
+                          DNSTypeName(swap16(rrsig->typeCovered)), rrsig->alg, rrsig->labels, swap32(rrsig->origTTL),
+                          expTimeBuf, inceptTimeBuf, swap16(rrsig->keyTag));
+            
+            q = (const unsigned char *)&rrsig->signerName;
+            k = p;
+            p += snprintd(p, rdb + rdb_size - p, &q);
+            len = p - k + 1;
+            
+            base64Encode(p, rdb + rdb_size - p, (unsigned char *)(rd + len + RRSIG_FIXED_SIZE), rdlen - (len + RRSIG_FIXED_SIZE));
+            break;
+        }
+    }
+    return;                           
+}
+
 static void DNSSD_API qr_reply(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode,
                                const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context)
 {
     char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv";
     const unsigned char *rd  = rdata;
     const unsigned char *end = (const unsigned char *) rdata + rdlen;
 static void DNSSD_API qr_reply(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode,
                                const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context)
 {
     char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv";
     const unsigned char *rd  = rdata;
     const unsigned char *end = (const unsigned char *) rdata + rdlen;
-    char rdb[1000] = "", *p = rdb;
+    char rdb[1000] = "0.0.0.0", *p = rdb;
     int unknowntype = 0;
     int unknowntype = 0;
+    char dnssec_status[15] = "Unknown";
+    char rr_type[RR_TYPE_SIZE];
+    char rr_class[3];
+    DNSServiceFlags check_flags = flags;//local flags for dnssec status checking
 
     (void)sdref;    // Unused
 
     (void)sdref;    // Unused
-    (void)flags;    // Unused
     (void)ifIndex;  // Unused
     (void)ttl;      // Unused
     (void)context;  // Unused
     EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode);
 
     (void)ifIndex;  // Unused
     (void)ttl;      // Unused
     (void)context;  // Unused
     EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode);
 
-    if (num_printed++ == 0) printf("Timestamp     A/R Flags if %-30s%4s%4s Rdata\n", "Name", "T", "C");
+    if (num_printed++ == 0)
+    {
+        if (operation == 'D') 
+            printf("Timestamp     A/R if %-30s%-6s%-7s%-18s Rdata\n", "Name", "Type", "Class", "DNSSECStatus"); 
+        else     
+            printf("Timestamp     A/R Flags if %-30s%-6s%-7s Rdata\n", "Name", "Type", "Class");
+    }
     printtimestamp();
 
     printtimestamp();
 
-    if (!errorCode)
+    switch (rrclass)
     {
     {
-        switch (rrtype)
-        {
-        case kDNSServiceType_A:
-            snprintf(rdb, sizeof(rdb), "%d.%d.%d.%d", rd[0], rd[1], rd[2], rd[3]);
+        case kDNSServiceClass_IN:
+            strncpy(rr_class, "IN", sizeof(rr_class));
             break;
             break;
-
-        case kDNSServiceType_NS:
-        case kDNSServiceType_CNAME:
-        case kDNSServiceType_PTR:
-        case kDNSServiceType_DNAME:
-            p += snprintd(p, sizeof(rdb), &rd);
+        default:
+            snprintf(rr_class, sizeof(rr_class), "%d", rrclass);
             break;
             break;
+    }
+    strncpy(rr_type, DNSTypeName(rrtype), sizeof(rr_type));
 
 
-        case kDNSServiceType_SOA:
-            p += snprintd(p, rdb + sizeof(rdb) - p, &rd);           // mname
-            p += snprintf(p, rdb + sizeof(rdb) - p, " ");
-            p += snprintd(p, rdb + sizeof(rdb) - p, &rd);           // rname
-            p += snprintf(p, rdb + sizeof(rdb) - p, " Ser %d Ref %d Ret %d Exp %d Min %d",
+    if (!errorCode) //to avoid printing garbage in rdata
+    {
+        if (!(check_flags & (kDNSServiceFlagsValidate | kDNSServiceFlagsValidateOptional)))
+        {
+            switch (rrtype)
+            {
+                case kDNSServiceType_A:
+                    snprintf(rdb, sizeof(rdb), "%d.%d.%d.%d", rd[0], rd[1], rd[2], rd[3]);
+                    break;
+    
+                case kDNSServiceType_NS:
+                case kDNSServiceType_CNAME:
+                case kDNSServiceType_PTR:
+                case kDNSServiceType_DNAME:
+                    p += snprintd(p, sizeof(rdb), &rd);
+                    break;
+
+                case kDNSServiceType_SOA:
+                    p += snprintd(p, rdb + sizeof(rdb) - p, &rd);           // mname
+                    p += snprintf(p, rdb + sizeof(rdb) - p, " ");
+                    p += snprintd(p, rdb + sizeof(rdb) - p, &rd);           // rname
+                    p += snprintf(p, rdb + sizeof(rdb) - p, " Ser %d Ref %d Ret %d Exp %d Min %d",
                           ntohl(((uint32_t*)rd)[0]), ntohl(((uint32_t*)rd)[1]), ntohl(((uint32_t*)rd)[2]), ntohl(((uint32_t*)rd)[3]), ntohl(((uint32_t*)rd)[4]));
                           ntohl(((uint32_t*)rd)[0]), ntohl(((uint32_t*)rd)[1]), ntohl(((uint32_t*)rd)[2]), ntohl(((uint32_t*)rd)[3]), ntohl(((uint32_t*)rd)[4]));
-            break;
+                    break;
 
 
-        case kDNSServiceType_AAAA:
-            snprintf(rdb, sizeof(rdb), "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X",
-                     rd[0x0], rd[0x1], rd[0x2], rd[0x3], rd[0x4], rd[0x5], rd[0x6], rd[0x7],
-                     rd[0x8], rd[0x9], rd[0xA], rd[0xB], rd[0xC], rd[0xD], rd[0xE], rd[0xF]);
-            break;
+                case kDNSServiceType_AAAA:
+                    snprintf(rdb, sizeof(rdb), "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X",
+                        rd[0x0], rd[0x1], rd[0x2], rd[0x3], rd[0x4], rd[0x5], rd[0x6], rd[0x7],
+                        rd[0x8], rd[0x9], rd[0xA], rd[0xB], rd[0xC], rd[0xD], rd[0xE], rd[0xF]);
+                    break;
 
 
-        case kDNSServiceType_SRV:
-            p += snprintf(p, rdb + sizeof(rdb) - p, "%d %d %d ",        // priority, weight, port
+                case kDNSServiceType_SRV:
+                    p += snprintf(p, rdb + sizeof(rdb) - p, "%d %d %d ",        // priority, weight, port
                           ntohs(*(unsigned short*)rd), ntohs(*(unsigned short*)(rd+2)), ntohs(*(unsigned short*)(rd+4)));
                           ntohs(*(unsigned short*)rd), ntohs(*(unsigned short*)(rd+2)), ntohs(*(unsigned short*)(rd+4)));
-            rd += 6;
-            p += snprintd(p, rdb + sizeof(rdb) - p, &rd);               // target host
-            break;
-
-        default: snprintf(rdb, sizeof(rdb), "%d bytes%s", rdlen, rdlen ? ":" : ""); unknowntype = 1; break;
+                    rd += 6;
+                    p += snprintd(p, rdb + sizeof(rdb) - p, &rd);               // target host
+                    break;
+
+                case kDNSServiceType_DS:
+                case kDNSServiceType_DNSKEY:
+                case kDNSServiceType_NSEC:
+                case kDNSServiceType_RRSIG:
+                    ParseDNSSECRecords(rrtype, rdb, p, rd, rdlen);
+                    break;
+
+                default: 
+                    snprintf(rdb, sizeof(rdb), "%d bytes%s", rdlen, rdlen ? ":" : ""); 
+                    unknowntype = 1;
+                    break;
+            }   
+        }   
+        else 
+        {
+            strncpy(rdb, "----", sizeof(rdb));
+            //Clear all o/p bits, and then check for dnssec status
+            check_flags &= ~kDNSServiceOutputFlags;
+            if (check_flags & kDNSServiceFlagsSecure)
+                strncpy(dnssec_status, "Secure", sizeof(dnssec_status));
+            else if (check_flags & kDNSServiceFlagsInsecure)
+                strncpy(dnssec_status, "Insecure", sizeof(dnssec_status));
+            else if (check_flags & kDNSServiceFlagsIndeterminate)
+                strncpy(dnssec_status, "Indeterminate", sizeof(dnssec_status));
+            else if (check_flags & kDNSServiceFlagsBogus) 
+                strncpy(dnssec_status, "Bogus", sizeof(dnssec_status));
         }
     }
 
         }
     }
 
-    printf("%s%6X%3d %-30s%4d%4d %s", op, flags, ifIndex, fullname, rrtype, rrclass, rdb);
-    if (unknowntype) while (rd < end) printf(" %02X", *rd++);
+    if (operation == 'D')
+        printf("%s%3d %-30s%-6s%-7s%-18s %s", op, ifIndex, fullname, rr_type, rr_class, dnssec_status, rdb);
+    else
+        printf("%s%6X%3d %-30s%-7s%-6s %s", op, flags, ifIndex, fullname, rr_type, rr_class, rdb);
+    if (unknowntype)
+    { 
+        while (rd < end) 
+            printf(" %02X", *rd++);
+    }
     if (errorCode)
     {
     if (errorCode)
     {
-        if (errorCode == kDNSServiceErr_NoSuchRecord) printf("No Such Record");
+        if (errorCode == kDNSServiceErr_NoSuchRecord) 
+            printf("    No Such Record");
         else if (errorCode == kDNSServiceErr_Timeout)
         {
         else if (errorCode == kDNSServiceErr_Timeout)
         {
-            printf("No Such Record\n");
+            printf("    No Such Record\n");
             printf("Query Timed Out\n");
             exit(1);
         }
             printf("Query Timed Out\n");
             exit(1);
         }
@@ -789,7 +1120,8 @@ static void DNSSD_API qr_reply(DNSServiceRef sdref, const DNSServiceFlags flags,
         if (flags & kDNSServiceFlagsAdd)
             DNSServiceReconfirmRecord(flags, ifIndex, fullname, rrtype, rrclass, rdlen, rdata);
 
         if (flags & kDNSServiceFlagsAdd)
             DNSServiceReconfirmRecord(flags, ifIndex, fullname, rrtype, rrclass, rdlen, rdata);
 
-    if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout);
+    if (!(flags & kDNSServiceFlagsMoreComing)) 
+        fflush(stdout);
 }
 
 #if HAS_NAT_PMP_API
 }
 
 #if HAS_NAT_PMP_API
@@ -817,17 +1149,26 @@ static void DNSSD_API port_mapping_create_reply(DNSServiceRef sdref, DNSServiceF
 #endif
 
 #if HAS_ADDRINFO_API
 #endif
 
 #if HAS_ADDRINFO_API
-static void DNSSD_API addrinfo_reply(DNSServiceRef sdref, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *hostname, const struct sockaddr *address, uint32_t ttl, void *context)
+static void DNSSD_API addrinfo_reply(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *hostname, const struct sockaddr *address, uint32_t ttl, void *context)
 {
     char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv";
     char addr[256] = "";
 {
     char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv";
     char addr[256] = "";
-    (void) sdref;
-    (void) context;
+    char dnssec_status[15] = "Unknown"; 
+    DNSServiceFlags check_flags = flags;
+       (void) sdref;
+       (void) context;
+
     EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode);
 
     EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode);
 
-    if (num_printed++ == 0) printf("Timestamp     A/R Flags if %-25s %-44s %s\n", "Hostname", "Address", "TTL");
+    if (num_printed++ == 0)
+    {
+        if (operation == 'g') 
+            printf("Timestamp     A/R if %-25s %-44s %-18s\n", "Hostname", "Address", "DNSSECStatus");
+        else  
+            printf("Timestamp     A/R Flags if %-38s %-44s %s\n", "Hostname", "Address", "TTL");
+    }
     printtimestamp();
     printtimestamp();
-
+   
     if (address && address->sa_family == AF_INET)
     {
         const unsigned char *b = (const unsigned char *) &((struct sockaddr_in *)address)->sin_addr;
     if (address && address->sa_family == AF_INET)
     {
         const unsigned char *b = (const unsigned char *) &((struct sockaddr_in *)address)->sin_addr;
@@ -840,20 +1181,42 @@ static void DNSSD_API addrinfo_reply(DNSServiceRef sdref, DNSServiceFlags flags,
         const unsigned char       *b  = (const unsigned char *      )&s6->sin6_addr;
         if (!if_indextoname(s6->sin6_scope_id, if_name))
             snprintf(if_name, sizeof(if_name), "<%d>", s6->sin6_scope_id);
         const unsigned char       *b  = (const unsigned char *      )&s6->sin6_addr;
         if (!if_indextoname(s6->sin6_scope_id, if_name))
             snprintf(if_name, sizeof(if_name), "<%d>", s6->sin6_scope_id);
-        snprintf(addr, sizeof(addr), "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X%%%s",
-                 b[0x0], b[0x1], b[0x2], b[0x3], b[0x4], b[0x5], b[0x6], b[0x7],
-                 b[0x8], b[0x9], b[0xA], b[0xB], b[0xC], b[0xD], b[0xE], b[0xF], if_name);
+            snprintf(addr, sizeof(addr), "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X%%%s",
+            b[0x0], b[0x1], b[0x2], b[0x3], b[0x4], b[0x5], b[0x6], b[0x7],
+            b[0x8], b[0x9], b[0xA], b[0xB], b[0xC], b[0xD], b[0xE], b[0xF], if_name);
     }
 
     }
 
-    printf("%s%6X%3d %-25s %-44s %d", op, flags, interfaceIndex, hostname, addr, ttl);
+    //go through this only if you have a dnssec validation status
+    if (!errorCode && (check_flags & (kDNSServiceFlagsValidate | kDNSServiceFlagsValidateOptional)))
+    {
+        strncpy(addr, "----", sizeof(addr));
+        //Clear all o/p bits, and then check for dnssec status
+        check_flags &= ~kDNSServiceOutputFlags;
+        if (check_flags & kDNSServiceFlagsSecure)
+            strncpy(dnssec_status, "Secure", sizeof(dnssec_status));
+        else if (check_flags & kDNSServiceFlagsInsecure)
+            strncpy(dnssec_status, "Insecure", sizeof(dnssec_status));
+        else if (check_flags & kDNSServiceFlagsIndeterminate)
+            strncpy(dnssec_status, "Indeterminate", sizeof(dnssec_status));
+        else if (check_flags & kDNSServiceFlagsBogus) 
+            strncpy(dnssec_status, "Bogus", sizeof(dnssec_status));
+    }
+    
+    if (operation == 'g')
+        printf("%s%3d %-25s %-44s %-18s", op, interfaceIndex, hostname, addr, dnssec_status);
+    else
+        printf("%s%6X%3d %-38s %-44s %d", op, flags, interfaceIndex, hostname, addr, ttl);
     if (errorCode)
     {
     if (errorCode)
     {
-        if (errorCode == kDNSServiceErr_NoSuchRecord) printf("   No Such Record");
-        else printf("   Error code %d", errorCode);
+        if (errorCode == kDNSServiceErr_NoSuchRecord) 
+            printf("   No Such Record");
+        else 
+            printf("   Error code %d", errorCode);
     }
     printf("\n");
 
     }
     printf("\n");
 
-    if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout);
+    if (!(flags & kDNSServiceFlagsMoreComing)) 
+        fflush(stdout);
 }
 #endif
 
 }
 #endif
 
@@ -1071,6 +1434,7 @@ int main(int argc, char **argv)
     char buffer[TypeBufferSize], *typ, *dom;
     int opi;
     DNSServiceFlags flags = 0;
     char buffer[TypeBufferSize], *typ, *dom;
     int opi;
     DNSServiceFlags flags = 0;
+    int optional = 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 --
 
     // 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 --
@@ -1132,6 +1496,52 @@ int main(int argc, char **argv)
         printf("Setting kDNSServiceFlagsBackgroundTrafficClass\n");
     }
 
         printf("Setting kDNSServiceFlagsBackgroundTrafficClass\n");
     }
 
+    if (argc > 1 && !strcasecmp(argv[1], "-t1"))
+    {
+        argc--;
+        argv++;
+        flags |= kDNSServiceFlagsThresholdOne;
+        printf("Setting kDNSServiceFlagsThresholdOne\n");
+    }
+
+    if (argc > 1 && !strcasecmp(argv[1], "-tFinder"))
+    {
+        argc--;
+        argv++;
+        flags |= kDNSServiceFlagsThresholdFinder;
+        printf("Setting kDNSServiceFlagsThresholdFinder\n");
+    }
+
+    if (argc > 1 && !strcasecmp(argv[1], "-wo"))
+    {
+        argc--;
+        argv++;
+        flags |= kDNSServiceFlagsWakeOnlyService;
+        printf("Setting kDNSServiceFlagsWakeOnlyService\n");
+    }
+
+    if (argc > 1 && !strcasecmp(argv[1], "-unicastResponse"))
+    {
+        argc--;
+        argv++;
+        flags |= kDNSServiceFlagsUnicastResponse;
+        printf("Setting kDNSServiceFlagsUnicastResponse\n");
+    }
+    if (argc > 1 && !strcasecmp(argv[1], "-timeout"))
+    {
+        argc--;
+        argv++;
+        flags |= kDNSServiceFlagsTimeout;
+        printf("Setting kDNSServiceFlagsTimeout\n");
+    }
+    if (argc > 1 && !strcasecmp(argv[1], "-optional"))
+    {
+        argc--;
+        argv++;
+        optional = 1;
+        printf("Setting DNSSEC optional flag\n");
+    }
+
     if (argc > 2 && !strcmp(argv[1], "-i"))
     {
         opinterface = if_nametoindex(argv[2]);
     if (argc > 2 && !strcmp(argv[1], "-i"))
     {
         opinterface = if_nametoindex(argv[2]);
@@ -1142,12 +1552,12 @@ int main(int argc, char **argv)
     }
 
     if (argc < 2) goto Fail;        // Minimum command line is the command name and one argument
     }
 
     if (argc < 2) goto Fail;        // Minimum command line is the command name and one argument
-    operation = getfirstoption(argc, argv, "EFBZLlRPQqtCAUNTMISVHh"
+    operation = getfirstoption(argc, argv, "EFBZLlRPQqCAUNTMISVHhD"
                                 #if HAS_NAT_PMP_API
                                "X"
                                 #endif
                                 #if HAS_ADDRINFO_API
                                 #if HAS_NAT_PMP_API
                                "X"
                                 #endif
                                 #if HAS_ADDRINFO_API
-                               "G"
+                               "Gg"
                                 #endif
                                , &opi);
     if (operation == -1) goto Fail;
                                 #endif
                                , &opi);
     if (operation == -1) goto Fail;
@@ -1207,6 +1617,7 @@ int main(int argc, char **argv)
         err = RegisterService(&client, argv[opi+0], typ, dom, NULL, argv[opi+3], argc-(opi+4), argv+(opi+4), flags);
         break;
 
         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); }
     case 'P':   if (argc < opi+6) goto Fail;
         err = DNSServiceCreateConnection(&client_pa);
         if (err) { fprintf(stderr, "DNSServiceCreateConnection returned %d\n", err); return(err); }
@@ -1215,18 +1626,28 @@ int main(int argc, char **argv)
         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;
 
         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 'D':
     case 'q':
     case 'Q':
     case 'C':   {
         uint16_t rrtype, rrclass;
         flags |= kDNSServiceFlagsReturnIntermediates;
     case 'q':
     case 'Q':
     case 'C':   {
         uint16_t rrtype, rrclass;
         flags |= kDNSServiceFlagsReturnIntermediates;
-        if (operation == 'q') flags |= kDNSServiceFlagsSuppressUnusable;
-        if (operation == 't') flags |= (kDNSServiceFlagsSuppressUnusable | kDNSServiceFlagsTimeout);
-        if (argc < opi+1) goto Fail;
+        if (operation == 'q') 
+            flags |= kDNSServiceFlagsSuppressUnusable;
+        if (argc < opi+1) 
+            goto Fail;
         rrtype = (argc <= opi+1) ? kDNSServiceType_A  : GetRRType(argv[opi+1]);
         rrtype = (argc <= opi+1) ? kDNSServiceType_A  : GetRRType(argv[opi+1]);
-        rrclass = (argc <= opi+2) ? kDNSServiceClass_IN : atoi(argv[opi+2]);
-        if (rrtype == kDNSServiceType_TXT || rrtype == kDNSServiceType_PTR) flags |= kDNSServiceFlagsLongLivedQuery;
+        rrclass = (argc <= opi+2) ? kDNSServiceClass_IN : GetRRClass(argv[opi+2]);
+        if (rrtype == kDNSServiceType_TXT || rrtype == kDNSServiceType_PTR)
+            flags |= kDNSServiceFlagsLongLivedQuery;
+        if (operation == 'D')
+        {
+            flags |= kDNSServiceFlagsSuppressUnusable;
+            if (optional)
+                flags |= kDNSServiceFlagsValidateOptional;
+            else
+                flags |= kDNSServiceFlagsValidate;
+        }
         err = DNSServiceQueryRecord(&client, flags, opinterface, argv[opi+0], rrtype, rrclass, qr_reply, NULL);
         break;
     }
         err = DNSServiceQueryRecord(&client, flags, opinterface, argv[opi+0], rrtype, rrclass, qr_reply, NULL);
         break;
     }
@@ -1293,10 +1714,21 @@ int main(int argc, char **argv)
 #endif
 
 #if HAS_ADDRINFO_API
 #endif
 
 #if HAS_ADDRINFO_API
+    case 'g':
     case 'G':   {
         flags |= kDNSServiceFlagsReturnIntermediates;
     case 'G':   {
         flags |= kDNSServiceFlagsReturnIntermediates;
-        if (argc != opi+2) goto Fail;
-        else err = DNSServiceGetAddrInfo(&client, flags, opinterface, GetProtocol(argv[opi+0]), argv[opi+1], addrinfo_reply, NULL);
+        if (operation == 'g')
+        {
+            flags |= kDNSServiceFlagsSuppressUnusable;
+            if (optional)
+                flags |= kDNSServiceFlagsValidateOptional;
+            else
+                flags |= kDNSServiceFlagsValidate;
+        }
+        if (argc != opi+2) 
+            goto Fail;
+        else 
+            err = DNSServiceGetAddrInfo(&client, flags, opinterface, GetProtocol(argv[opi+0]), argv[opi+1], addrinfo_reply, NULL);
         break;
     }
 #endif
         break;
     }
 #endif
@@ -1340,7 +1772,7 @@ int main(int argc, char **argv)
         uint32_t size = sizeof(v);
         err = DNSServiceGetProperty(kDNSServiceProperty_DaemonVersion, &v, &size);
         if (err) fprintf(stderr, "DNSServiceGetProperty failed %ld\n", (long int)err);
         uint32_t size = sizeof(v);
         err = DNSServiceGetProperty(kDNSServiceProperty_DaemonVersion, &v, &size);
         if (err) fprintf(stderr, "DNSServiceGetProperty failed %ld\n", (long int)err);
-        else printf("Currently running daemon (system service) is version %d.%d\n", v / 10000, v / 100 % 100);
+        else printf("Currently running daemon (system service) is version %d.%d.%d\n",  v / 10000, v / 100 % 100, v % 100);
         exit(0);
     }
 
         exit(0);
     }
 
diff --git a/Clients/dnsctl.c b/Clients/dnsctl.c
new file mode 100644 (file)
index 0000000..f040e1f
--- /dev/null
@@ -0,0 +1,177 @@
+/* -*- Mode: C; tab-width: 4 -*- 
+ *
+ * Copyright (c) 2012 Apple Inc. All rights reserved.
+ *
+ * dnsctl.c 
+ * Command-line tool using libdns_services.dylib 
+ *   
+ * To build only this tool, copy and paste the following on the command line:
+ * On Apple 64bit Platforms ONLY OSX/iOS:
+ * clang -Wall dnsctl.c /usr/lib/libdns_services.dylib -o dnsctl
+ * 
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <net/if.h> // if_nametoindex()
+
+#include <dispatch/dispatch.h>
+#include "dns_services.h"
+
+//*************************************************************************************************************
+// Globals:
+//*************************************************************************************************************
+
+static const char kFilePathSep   =  '/';
+static DNSXConnRef ClientRef     =  NULL;
+
+//*************************************************************************************************************
+// Utility Funcs:
+//*************************************************************************************************************
+
+static void printtimestamp(void) 
+{
+    struct tm tm; 
+    int ms; 
+    static char date[16];
+    static char new_date[16];
+    struct timeval tv; 
+    gettimeofday(&tv, NULL);
+    localtime_r((time_t*)&tv.tv_sec, &tm);
+    ms = tv.tv_usec/1000;
+    strftime(new_date, sizeof(new_date), "%a %d %b %Y", &tm);
+    //display date only if it has changed
+    if (strncmp(date, new_date, sizeof(new_date)))
+    {        
+        printf("DATE: ---%s---\n", new_date);
+        strncpy(date, new_date, sizeof(date));
+    }        
+    printf("%2d:%02d:%02d.%03d  ", tm.tm_hour, tm.tm_min, tm.tm_sec, ms); 
+}
+
+static void print_usage(const char *arg0)
+{
+    fprintf(stderr, "%s USAGE:                                                                  \n", arg0);
+    fprintf(stderr, "%s -DP Enable DNS Proxy with Default Parameters                            \n", arg0);
+    fprintf(stderr, "%s -DP [-o <output interface>] [-i <input interface(s)>] Enable DNS Proxy  \n", arg0);
+}
+
+//*************************************************************************************************************
+// CallBack Funcs:
+//*************************************************************************************************************
+
+// DNSXEnableProxy Callback from the Daemon
+static void dnsproxy_reply(DNSXConnRef connRef, DNSXErrorType errCode)
+{
+    (void) connRef;
+    printtimestamp();
+    switch (errCode)
+    {
+        case kDNSX_NoError          :  printf("  SUCCESS   \n");     break;
+        case kDNSX_DictError        :  printf(" DICT ERROR \n");     break;
+        case kDNSX_DaemonNotRunning :  printf(" NO DAEMON  \n");
+                                       DNSXRefDeAlloc(ClientRef);    break;
+        case kDNSX_Engaged          :  printf(" ENGAGED    \n");
+                                       DNSXRefDeAlloc(ClientRef);    break;
+        case kDNSX_UnknownErr       :
+        default                     :  printf("UNKNOWN ERR \n");
+                                       DNSXRefDeAlloc(ClientRef);    break;
+    }
+
+}
+
+//*************************************************************************************************************
+
+int main(int argc, char **argv)
+{
+    DNSXErrorType err;
+
+    // Default i/p intf is lo0 and o/p intf is primary interface
+    IfIndex Ipintfs[MaxInputIf] =  {1, 0, 0, 0, 0};
+    IfIndex Opintf = kDNSIfindexAny;
+
+    // Extract program name from argv[0], which by convention contains the path to this executable
+    const char *a0 = strrchr(argv[0], kFilePathSep) + 1; 
+    if (a0 == (const char *)1)
+        a0 = argv[0];
+
+    // Must run as root
+    if (0 != geteuid()) 
+    {        
+        fprintf(stderr, "%s MUST run as root!!\n", a0); 
+        exit(-1); 
+    }
+    if ((sizeof(argv) == 8))
+        printf("dnsctl running in 64-bit mode\n");
+    else if ((sizeof(argv) == 4))
+        printf("dnsctl running in 32-bit mode\n");
+
+    // expects atleast one argument
+    if (argc < 2)
+        goto Usage;
+
+    if ( !strcmp(argv[1], "-DP") || !strcmp(argv[1], "-dp") )
+    {
+        if (argc == 2)
+        {
+            printtimestamp();
+            printf("Proceeding to Enable DNSProxy on mDNSResponder with Default Parameters\n");
+            dispatch_queue_t my_Q = dispatch_queue_create("com.apple.dnsctl.callback_queue", NULL);
+            err = DNSXEnableProxy(&ClientRef, kDNSProxyEnable, Ipintfs, Opintf, my_Q, dnsproxy_reply);
+        }            
+        else if (argc > 2)
+        {
+            argc--;
+            argv++;
+            if (!strcmp(argv[1], "-o"))
+            {
+                Opintf = if_nametoindex(argv[2]);
+                if (!Opintf) 
+                    Opintf = atoi(argv[2]);
+                if (!Opintf) 
+                { 
+                    fprintf(stderr, "Could not parse o/p interface [%s]: Passing default primary \n", argv[2]); 
+                    Opintf = kDNSIfindexAny;
+                }
+                argc -= 2;
+                argv += 2;
+            }
+            if (argc > 2 && !strcmp(argv[1], "-i")) 
+            {
+                int i;
+                argc--;
+                argv++;
+                for (i = 0; i < MaxInputIf && argc > 1; i++)
+                {
+                    Ipintfs[i] = if_nametoindex(argv[1]);
+                    if (!Ipintfs[i])
+                        Ipintfs[i] = atoi(argv[1]);  
+                    if (!Ipintfs[i])
+                    {
+                        fprintf(stderr, "Could not parse i/p interface [%s]: Passing default lo0 \n", argv[2]); 
+                        Ipintfs[i] = 1;
+                    }
+                    argc--;
+                    argv++;
+                }
+            }  
+            printtimestamp();
+            printf("Proceeding to Enable DNSProxy on mDNSResponder \n");
+            dispatch_queue_t my_Q = dispatch_queue_create("com.apple.dnsctl.callback_queue", NULL);
+            err = DNSXEnableProxy(&ClientRef, kDNSProxyEnable, Ipintfs, Opintf, my_Q, dnsproxy_reply);                
+        }
+    }
+    else
+    {
+        goto Usage;
+    }
+
+    dispatch_main(); 
+
+Usage:
+    print_usage(a0);
+    return 0;
+}
+
index c714dea1b906f7fa0975116e473291f193bdfdc3..89a27a4d5add50114c107a6e36937bad50e76d7a 100755 (executable)
@@ -195,6 +195,8 @@ xcopy /I/Y "$(TargetPath)"
     </PostBuildEvent>\r
   </ItemDefinitionGroup>\r
   <ItemGroup>\r
     </PostBuildEvent>\r
   </ItemDefinitionGroup>\r
   <ItemGroup>\r
+    <ClCompile Include="..\..\mDNSCore\anonymous.c" />\r
+    <ClCompile Include="..\..\mDNSCore\CryptoAlg.c" />\r
     <ClCompile Include="..\..\mDNSCore\DNSCommon.c" />\r
     <ClCompile Include="..\..\mDNSCore\DNSDigest.c" />\r
     <ClCompile Include="..\..\mDNSShared\dnssd_ipc.c" />\r
     <ClCompile Include="..\..\mDNSCore\DNSCommon.c" />\r
     <ClCompile Include="..\..\mDNSCore\DNSDigest.c" />\r
     <ClCompile Include="..\..\mDNSShared\dnssd_ipc.c" />\r
@@ -209,6 +211,8 @@ xcopy /I/Y "$(TargetPath)"
     <ClCompile Include="..\..\mDNSCore\uDNS.c" />\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClCompile Include="..\..\mDNSCore\uDNS.c" />\r
   </ItemGroup>\r
   <ItemGroup>\r
+    <ClInclude Include="..\..\mDNSCore\anonymous.h" />\r
+    <ClInclude Include="..\..\mDNSCore\CryptoAlg.h" />\r
     <ClInclude Include="..\..\mDNSCore\DNSCommon.h" />\r
     <ClInclude Include="..\..\mDNSShared\dnssd_ipc.h" />\r
     <ClInclude Include="..\..\mDNSWindows\Poll.h" />\r
     <ClInclude Include="..\..\mDNSCore\DNSCommon.h" />\r
     <ClInclude Include="..\..\mDNSShared\dnssd_ipc.h" />\r
     <ClInclude Include="..\..\mDNSWindows\Poll.h" />\r
index 8a60f089dffc947197d54b4dc7a88f88f85682b3..b9a25beb9cab259e70d7c7229742978a59b23814 100755 (executable)
     <ClCompile Include="..\..\mDNSWindows\Poll.c">\r
       <Filter>Source Files</Filter>\r
     </ClCompile>\r
     <ClCompile Include="..\..\mDNSWindows\Poll.c">\r
       <Filter>Source Files</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="..\..\mDNSCore\anonymous.c">\r
+      <Filter>Source Files</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\..\mDNSCore\CryptoAlg.c">\r
+      <Filter>Source Files</Filter>\r
+    </ClCompile>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="..\..\mDNSCore\DNSCommon.h">\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="..\..\mDNSCore\DNSCommon.h">\r
     <ClInclude Include="..\..\mDNSWindows\Poll.h">\r
       <Filter>Header Files</Filter>\r
     </ClInclude>\r
     <ClInclude Include="..\..\mDNSWindows\Poll.h">\r
       <Filter>Header Files</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="..\..\mDNSCore\anonymous.h">\r
+      <Filter>Header Files</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="..\..\mDNSCore\CryptoAlg.h">\r
+      <Filter>Header Files</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ResourceCompile Include="mDNSNetMonitor.rc">\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ResourceCompile Include="mDNSNetMonitor.rc">\r
index 016b5eefeac5c0738a1ff27e24be2eb4bf9d85a4..7d7846081f8efccf55cbca022278caf562617516 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -16,7 +16,7 @@
 
 include $(MAKEFILEPATH)/pb_makefiles/platform.make
 
 
 include $(MAKEFILEPATH)/pb_makefiles/platform.make
 
-MVERS = "mDNSResponder-379.38.1"
+MVERS = "mDNSResponder-522.1.11"
 
 DDNSWRITECONFIG = "$(DSTROOT)/Library/Application Support/Bonjour/ddnswriteconfig"
 VER = 
 
 DDNSWRITECONFIG = "$(DSTROOT)/Library/Application Support/Bonjour/ddnswriteconfig"
 VER = 
@@ -42,5 +42,8 @@ installsrc:
 installhdrs::
        cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild installhdrs OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) SDKROOT=$(SDKROOT)  -target SystemLibraries $(VER)
 
 installhdrs::
        cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild installhdrs OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) SDKROOT=$(SDKROOT)  -target SystemLibraries $(VER)
 
+java:
+       cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild install  OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) SDKROOT=$(SDKROOT) -target libjdns_sd.jnilib $(VER)
+
 clean::
        echo clean
 clean::
        echo clean
index 0fee771ff44c85c460d6b8d67d20da717298ed2a..38533fc8864b3b7be593a373359c7e44b2548b7a 100644 (file)
@@ -183,7 +183,7 @@ mDNSexport mDNSu32 AlgLength(AlgContext *ctx)
         return 0;
 }
 
         return 0;
 }
 
-mDNSexport mStatus AlgAdd(AlgContext *ctx, void *data, mDNSu32 len)
+mDNSexport mStatus AlgAdd(AlgContext *ctx, const void *data, mDNSu32 len)
 {
     AlgFuncs *func = mDNSNULL;
 
 {
     AlgFuncs *func = mDNSNULL;
 
@@ -254,3 +254,27 @@ mDNSexport mDNSu8* AlgEncode(AlgContext *ctx)
     else
         return mDNSNULL;
 }
     else
         return mDNSNULL;
 }
+
+mDNSexport mStatus AlgFinal(AlgContext *ctx, void *data, mDNSu32 len)
+{
+    AlgFuncs *func = mDNSNULL;
+
+    if (ctx->type == CRYPTO_ALG)
+        func = CryptoAlgFuncs[ctx->alg];
+    else if (ctx->type == DIGEST_ALG)
+        func = DigestAlgFuncs[ctx->alg];
+    else if (ctx->type == ENC_ALG)
+        func = EncAlgFuncs[ctx->alg];
+
+    // This should never happen as AlgCreate would have failed
+    if (!func)
+    {
+        LogMsg("AlgEncode: ERROR!! func is NULL");
+        return mDNSNULL;
+    }
+
+    if (func->Final)
+        return (func->Final(ctx, data, len));
+    else
+        return mStatus_BadParamErr;
+}
index 4ed687925b72ad238ab19689c2be1586384fcd1f..6cb3dc9d2482219dff1e80bf0dcedbff0c127fdb 100644 (file)
@@ -36,11 +36,13 @@ typedef struct
     mStatus (*Create)(AlgContext *ctx);
     mStatus (*Destroy)(AlgContext *ctx);
     mDNSu32 (*Length)(AlgContext *ctx);
     mStatus (*Create)(AlgContext *ctx);
     mStatus (*Destroy)(AlgContext *ctx);
     mDNSu32 (*Length)(AlgContext *ctx);
-    mStatus (*Add)(AlgContext *ctx, void *data, mDNSu32 len);
+    mStatus (*Add)(AlgContext *ctx, const void *data, mDNSu32 len);
     // Verify the ctx using the key and compare it against signature/siglen
     mStatus (*Verify)(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen);
     // Encode the data and return the encoded data
     mDNSu8* (*Encode)(AlgContext *ctx);
     // Verify the ctx using the key and compare it against signature/siglen
     mStatus (*Verify)(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen);
     // Encode the data and return the encoded data
     mDNSu8* (*Encode)(AlgContext *ctx);
+    // Return the finalized data in data whose length is len (used by hash algorithms)
+    mStatus (*Final)(AlgContext *ctx, void *data, mDNSu32 len);
 } AlgFuncs;
 
 mDNSexport mStatus DigestAlgInit(mDNSu8 digestType, AlgFuncs *func);
 } AlgFuncs;
 
 mDNSexport mStatus DigestAlgInit(mDNSu8 digestType, AlgFuncs *func);
@@ -51,8 +53,9 @@ mDNSexport mStatus EncAlgInit(mDNSu8 algType, AlgFuncs *func);
 extern AlgContext *AlgCreate(AlgType type, mDNSu8 alg);
 extern mStatus AlgDestroy(AlgContext *ctx);
 extern mDNSu32 AlgLength(AlgContext *ctx);
 extern AlgContext *AlgCreate(AlgType type, mDNSu8 alg);
 extern mStatus AlgDestroy(AlgContext *ctx);
 extern mDNSu32 AlgLength(AlgContext *ctx);
-extern mStatus AlgAdd(AlgContext *ctx, void *data, mDNSu32 len);
+extern mStatus AlgAdd(AlgContext *ctx, const void *data, mDNSu32 len);
 extern mStatus AlgVerify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen);
 extern mDNSu8* AlgEncode(AlgContext *ctx);
 extern mStatus AlgVerify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen);
 extern mDNSu8* AlgEncode(AlgContext *ctx);
+extern mStatus AlgFinal(AlgContext *ctx, void *data, mDNSu32 len);
 
 #endif // __CRYPTO_ALG_H
 
 #endif // __CRYPTO_ALG_H
index 99429eacb77477cc46a52ccc29b49ac912fa9316..c8fcc8987b4bc04e1f62b610d248e16e59e97837 100644 (file)
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 4 -*-
  *
 /* -*- Mode: C; tab-width: 4 -*-
  *
- * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2002-2013 Apple Computer, 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.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
 #define mDNS_InstantiateInlines 1
 #include "DNSCommon.h"
 #include "CryptoAlg.h"
 #define mDNS_InstantiateInlines 1
 #include "DNSCommon.h"
 #include "CryptoAlg.h"
+#include "anonymous.h"
 
 // Disable certain benign warnings with Microsoft compilers
 #if (defined(_MSC_VER))
 
 // Disable certain benign warnings with Microsoft compilers
 #if (defined(_MSC_VER))
@@ -41,6 +42,7 @@ mDNSexport const mDNSInterfaceID mDNSInterfaceMark       = (mDNSInterfaceID)-1;
 mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)-2;
 mDNSexport const mDNSInterfaceID mDNSInterface_Unicast   = (mDNSInterfaceID)-3;
 mDNSexport const mDNSInterfaceID mDNSInterface_P2P       = (mDNSInterfaceID)-4;
 mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)-2;
 mDNSexport const mDNSInterfaceID mDNSInterface_Unicast   = (mDNSInterfaceID)-3;
 mDNSexport const mDNSInterfaceID mDNSInterface_P2P       = (mDNSInterfaceID)-4;
+mDNSexport const mDNSInterfaceID uDNSInterfaceMark       = (mDNSInterfaceID)-5;
 
 // Note: Microsoft's proposed "Link Local Multicast Name Resolution Protocol" (LLMNR) is essentially a limited version of
 // Multicast DNS, using the same packet formats, naming syntax, and record types as Multicast DNS, but on a different UDP
 
 // Note: Microsoft's proposed "Link Local Multicast Name Resolution Protocol" (LLMNR) is essentially a limited version of
 // Multicast DNS, using the same packet formats, naming syntax, and record types as Multicast DNS, but on a different UDP
@@ -88,7 +90,7 @@ mDNSexport const mDNSEthAddr onesEthAddr       = { { 255, 255, 255, 255, 255, 25
 mDNSexport const mDNSAddr zeroAddr          = { mDNSAddrType_None, {{{ 0 }}} };
 
 mDNSexport const mDNSv4Addr AllDNSAdminGroup   = { { 239, 255, 255, 251 } };
 mDNSexport const mDNSAddr zeroAddr          = { mDNSAddrType_None, {{{ 0 }}} };
 
 mDNSexport const mDNSv4Addr AllDNSAdminGroup   = { { 239, 255, 255, 251 } };
-mDNSexport const mDNSv4Addr AllHosts_v4        = { { 224,   0,   0,   1 } };  // For NAT-PMP Annoucements
+mDNSexport const mDNSv4Addr AllHosts_v4        = { { 224,   0,   0,   1 } };  // For NAT-PMP & PCP Annoucements
 mDNSexport const mDNSv6Addr AllHosts_v6        = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01 } };
 mDNSexport const mDNSv6Addr NDP_prefix         = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01, 0xFF,0x00,0x00,0xFB } };  // FF02:0:0:0:0:1:FF00::/104
 mDNSexport const mDNSEthAddr AllHosts_v6_Eth    = { { 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 } };
 mDNSexport const mDNSv6Addr AllHosts_v6        = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01 } };
 mDNSexport const mDNSv6Addr NDP_prefix         = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01, 0xFF,0x00,0x00,0xFB } };  // FF02:0:0:0:0:1:FF00::/104
 mDNSexport const mDNSEthAddr AllHosts_v6_Eth    = { { 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 } };
@@ -115,13 +117,34 @@ mDNSexport const mDNSOpaque64 zeroOpaque64    = { { 0 } };
 #endif
 
 // return true for RFC1918 private addresses
 #endif
 
 // return true for RFC1918 private addresses
-mDNSexport mDNSBool mDNSv4AddrIsRFC1918(mDNSv4Addr *addr)
+mDNSexport mDNSBool mDNSv4AddrIsRFC1918(const mDNSv4Addr * const addr)
 {
     return ((addr->b[0] == 10) ||                                 // 10/8 prefix
             (addr->b[0] == 172 && (addr->b[1] & 0xF0) == 16) ||   // 172.16/12
             (addr->b[0] == 192 && addr->b[1] == 168));            // 192.168/16
 }
 
 {
     return ((addr->b[0] == 10) ||                                 // 10/8 prefix
             (addr->b[0] == 172 && (addr->b[1] & 0xF0) == 16) ||   // 172.16/12
             (addr->b[0] == 192 && addr->b[1] == 168));            // 192.168/16
 }
 
+mDNSexport void mDNSAddrMapIPv4toIPv6(mDNSv4Addr* in, mDNSv6Addr* out)
+{
+    out->l[0] = 0;
+    out->l[1] = 0;
+    out->w[4] = 0;
+    out->w[5] = 0xffff;
+    out->b[12] = in->b[0];
+    out->b[13] = in->b[1];
+    out->b[14] = in->b[2];
+    out->b[15] = in->b[3];
+}
+
+mDNSexport mDNSBool mDNSAddrIPv4FromMappedIPv6(mDNSv6Addr *in, mDNSv4Addr* out)
+{
+    if (in->l[0] != 0 || in->l[1] != 0 || in->w[4] != 0 || in->w[5] != 0xffff)
+        return mDNSfalse;
+
+    out->NotAnInteger = in->l[3];
+    return mDNStrue;
+}
+
 mDNSexport NetworkInterfaceInfo *GetFirstActiveInterface(NetworkInterfaceInfo *intf)
 {
     while (intf && !intf->InterfaceActive) intf = intf->next;
 mDNSexport NetworkInterfaceInfo *GetFirstActiveInterface(NetworkInterfaceInfo *intf)
 {
     while (intf && !intf->InterfaceActive) intf = intf->next;
@@ -163,6 +186,8 @@ mDNSexport char *DNSTypeName(mDNSu16 rrtype)
     case kDNSType_SRV:  return("SRV");
     case kDNSType_OPT:  return("OPT");
     case kDNSType_NSEC: return("NSEC");
     case kDNSType_SRV:  return("SRV");
     case kDNSType_OPT:  return("OPT");
     case kDNSType_NSEC: return("NSEC");
+    case kDNSType_NSEC3: return("NSEC3");
+    case kDNSType_NSEC3PARAM: return("NSEC3PARAM");
     case kDNSType_TSIG: return("TSIG");
     case kDNSType_RRSIG: return("RRSIG");
     case kDNSType_DNSKEY: return("DNSKEY");
     case kDNSType_TSIG: return("TSIG");
     case kDNSType_RRSIG: return("RRSIG");
     case kDNSType_DNSKEY: return("DNSKEY");
@@ -199,11 +224,12 @@ mDNSlocal char *DNSSECDigestName(mDNSu8 digest)
     {
     case SHA1_DIGEST_TYPE: return "SHA1";
     case SHA256_DIGEST_TYPE: return "SHA256";
     {
     case SHA1_DIGEST_TYPE: return "SHA1";
     case SHA256_DIGEST_TYPE: return "SHA256";
-    default: {
+    default:
+        {
         static char digbuffer[16];
         mDNS_snprintf(digbuffer, sizeof(digbuffer), "DIG%d", digest);
         return(digbuffer);
         static char digbuffer[16];
         mDNS_snprintf(digbuffer, sizeof(digbuffer), "DIG%d", digest);
         return(digbuffer);
-    }
+        }
     }
 }
 
     }
 }
 
@@ -240,27 +266,100 @@ mDNSlocal mDNSu32 keytag(mDNSu8 *key, mDNSu32 keysize)
     return ac & 0xFFFF;
 }
 
     return ac & 0xFFFF;
 }
 
-mDNSlocal int base64Encode(char *buffer, int blen, mDNSu8 *data, int len)
+mDNSexport int baseEncode(char *buffer, int blen, const mDNSu8 *data, int len, int encAlg)
 {
     AlgContext *ctx;
     mDNSu8 *outputBuffer;
     int length;
 
 {
     AlgContext *ctx;
     mDNSu8 *outputBuffer;
     int length;
 
-    ctx = AlgCreate(ENC_ALG, ENC_BASE64);
+    ctx = AlgCreate(ENC_ALG, encAlg);
     if (!ctx)
     {
     if (!ctx)
     {
-        LogMsg("base64Encode: AlgCreate failed\n");
+        LogMsg("baseEncode: AlgCreate failed\n");
         return 0;
     }
     AlgAdd(ctx, data, len);
     outputBuffer = AlgEncode(ctx);
     length = 0;
     if (outputBuffer)
         return 0;
     }
     AlgAdd(ctx, data, len);
     outputBuffer = AlgEncode(ctx);
     length = 0;
     if (outputBuffer)
-        length = mDNS_snprintf(buffer, blen, "  %s", outputBuffer);
+    {
+        // Note: don't include any spaces in the format string below. This
+        // is also used by NSEC3 code for proving non-existence where it
+        // needs the base32 encoding without any spaces etc.
+        length = mDNS_snprintf(buffer, blen, "%s", outputBuffer);
+    }
     AlgDestroy(ctx);
     return length;
 }
 
     AlgDestroy(ctx);
     return length;
 }
 
+mDNSlocal void PrintTypeBitmap(const mDNSu8 *bmap, int bitmaplen, char *const buffer, mDNSu32 length)
+{
+    int win, wlen, type;
+
+    while (bitmaplen > 0)
+    {
+        int i;
+
+        if (bitmaplen < 3)
+        {
+            LogMsg("PrintTypeBitmap: malformed bitmap, bitmaplen %d short", bitmaplen);
+            break;
+        }
+
+        win = *bmap++;
+        wlen = *bmap++;
+        bitmaplen -= 2;
+        if (bitmaplen < wlen || wlen < 1 || wlen > 32)
+        {
+            LogInfo("PrintTypeBitmap: malformed nsec, bitmaplen %d wlen %d", bitmaplen, wlen);
+            break;
+        }
+        if (win < 0 || win >= 256)
+        {
+            LogInfo("PrintTypeBitmap: malformed nsec, bad window win %d", win);
+            break;
+        }
+        type = win * 256;
+        for (i = 0; i < wlen * 8; i++)
+        {
+            if (bmap[i>>3] & (128 >> (i&7)))
+                length += mDNS_snprintf(buffer+length, (MaxMsg - 1) - length, "%s ", DNSTypeName(type + i));
+        }
+        bmap += wlen;
+        bitmaplen -= wlen;
+    }
+}
+
+// Parse the fields beyond the base header. NSEC3 should have been validated.
+mDNSexport void NSEC3Parse(const ResourceRecord *const rr, mDNSu8 **salt, int *hashLength, mDNSu8 **nxtName, int *bitmaplen, mDNSu8 **bitmap)
+{
+       const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
+       rdataNSEC3 *nsec3 = (rdataNSEC3 *)rdb->data;
+    mDNSu8 *p = (mDNSu8 *)&nsec3->salt;
+    int hlen;
+
+    if (salt)
+    {
+        if (nsec3->saltLength)
+            *salt = p;
+        else
+            *salt = mDNSNULL;
+    }
+    p += nsec3->saltLength;
+    // p is pointing at hashLength
+    hlen = (int)*p;
+    if (hashLength)
+        *hashLength = hlen;
+    p++;
+    if (nxtName)
+        *nxtName = p;
+    p += hlen;
+    if (bitmaplen)
+        *bitmaplen = rr->rdlength - (int)(p - rdb->data);
+    if (bitmap)
+        *bitmap = p;
+}
+
 // Note slight bug: this code uses the rdlength from the ResourceRecord object, to display
 // the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as
 // long as this routine is only used for debugging messages, it probably isn't a big problem.
 // Note slight bug: this code uses the rdlength from the ResourceRecord object, to display
 // the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as
 // long as this routine is only used for debugging messages, it probably isn't a big problem.
@@ -329,6 +428,10 @@ mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RD
                         length += mDNS_snprintf(buffer+length, RemSpc, " Password %.6a", opt->u.owner.password.b);
                 }
                 break;
                         length += mDNS_snprintf(buffer+length, RemSpc, " Password %.6a", opt->u.owner.password.b);
                 }
                 break;
+            case kDNSOpt_Trace:
+                length += mDNS_snprintf(buffer+length, RemSpc, " Platform %d",     opt->u.tracer.platf);
+                length += mDNS_snprintf(buffer+length, RemSpc, " mDNSVers %d",     opt->u.tracer.mDNSv);
+                break;
             default:
                 length += mDNS_snprintf(buffer+length, RemSpc, " Unknown %d",  opt->opt);
                 break;
             default:
                 length += mDNS_snprintf(buffer+length, RemSpc, " Unknown %d",  opt->opt);
                 break;
@@ -340,7 +443,6 @@ mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RD
     case kDNSType_NSEC: {
         domainname *next = (domainname *)rd->data;
         int len, bitmaplen;
     case kDNSType_NSEC: {
         domainname *next = (domainname *)rd->data;
         int len, bitmaplen;
-        int win, wlen, type;
         mDNSu8 *bmap;
         len = DomainNameLength(next);
         bitmaplen = rr->rdlength - len;
         mDNSu8 *bmap;
         len = DomainNameLength(next);
         bitmaplen = rr->rdlength - len;
@@ -348,39 +450,45 @@ mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RD
 
         if (UNICAST_NSEC(rr))
             length += mDNS_snprintf(buffer+length, RemSpc, "%##s ", next->c);
 
         if (UNICAST_NSEC(rr))
             length += mDNS_snprintf(buffer+length, RemSpc, "%##s ", next->c);
+        PrintTypeBitmap(bmap, bitmaplen, buffer, length);
 
 
-        while (bitmaplen > 0)
+    }
+    break;
+    case kDNSType_NSEC3: {
+        rdataNSEC3 *nsec3 = (rdataNSEC3 *)rd->data;
+        const mDNSu8 *p = (mDNSu8 *)&nsec3->salt;
+        int hashLength, bitmaplen, i;
+
+        length += mDNS_snprintf(buffer+length, RemSpc, "\t%s  %d  %d ", 
+                                DNSSECDigestName(nsec3->alg), nsec3->flags, swap16(nsec3->iterations));
+        
+        if (!nsec3->saltLength)
         {
         {
-            int i;
-
-            if (bitmaplen < 3)
-            {
-                LogMsg("GetRRDisplayString_rdb: malformed nsec, bitmaplen %d short", bitmaplen);
-                break;
-            }
-
-            win = *bmap++;
-            wlen = *bmap++;
-            bitmaplen -= 2;
-            if (bitmaplen < wlen || wlen < 1 || wlen > 32)
-            {
-                LogInfo("GetRRDisplayString_rdb: malformed nsec, bitmaplen %d wlen %d", bitmaplen, wlen);
-                break;
-            }
-            if (win < 0 || win >= 256)
-            {
-                LogInfo("GetRRDisplayString_rdb: malformed nsec, bad window win %d", win);
-                break;
-            }
-            type = win * 256;
-            for (i = 0; i < wlen * 8; i++)
+            length += mDNS_snprintf(buffer+length, RemSpc, "-");
+        }
+        else
+        {
+            for (i = 0; i < nsec3->saltLength; i++)
             {
             {
-                if (bmap[i>>3] & (128 >> (i&7)))
-                    length += mDNS_snprintf(buffer+length, RemSpc, "%s ", DNSTypeName(type + i));
+                length += mDNS_snprintf(buffer+length, RemSpc, "%x", p[i]);
             }
             }
-            bmap += wlen;
-            bitmaplen -= wlen;
         }
         }
+
+        // put a space at the end
+        length += mDNS_snprintf(buffer+length, RemSpc, " ");
+
+        p += nsec3->saltLength;
+        // p is pointing at hashLength
+        hashLength = (int)*p++;
+        
+        length += baseEncode(buffer + length, RemSpc, p, hashLength, ENC_BASE32);
+
+        // put a space at the end
+        length += mDNS_snprintf(buffer+length, RemSpc, " ");
+
+        p += hashLength;
+        bitmaplen = rr->rdlength - (int)(p - rd->data);
+        PrintTypeBitmap(p, bitmaplen, buffer, length);
     }
     break;
     case kDNSType_RRSIG:    {
     }
     break;
     case kDNSType_RRSIG:    {
@@ -397,21 +505,21 @@ mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RD
         inceptClock = (unsigned long)swap32(rrsig->sigInceptTime);
         mDNSPlatformFormatTime(inceptClock, inceptTimeBuf, sizeof(inceptTimeBuf));
 
         inceptClock = (unsigned long)swap32(rrsig->sigInceptTime);
         mDNSPlatformFormatTime(inceptClock, inceptTimeBuf, sizeof(inceptTimeBuf));
 
-        length += mDNS_snprintf(buffer+length, RemSpc, "\t%s  %s  %d  %d  %s  %s  %d  %##s",
+        length += mDNS_snprintf(buffer+length, RemSpc, "\t%s  %s  %d  %d  %s  %s  %d  %##s ",
                                 DNSTypeName(swap16(rrsig->typeCovered)), DNSSECAlgName(rrsig->alg), rrsig->labels, swap32(rrsig->origTTL),
                                 expTimeBuf, inceptTimeBuf, swap16(rrsig->keyTag), ((domainname *)(&rrsig->signerName))->c);
 
         len = DomainNameLength((domainname *)&rrsig->signerName);
                                 DNSTypeName(swap16(rrsig->typeCovered)), DNSSECAlgName(rrsig->alg), rrsig->labels, swap32(rrsig->origTTL),
                                 expTimeBuf, inceptTimeBuf, swap16(rrsig->keyTag), ((domainname *)(&rrsig->signerName))->c);
 
         len = DomainNameLength((domainname *)&rrsig->signerName);
-        length += base64Encode(buffer + length, RemSpc, (mDNSu8 *)(rd->data + len + RRSIG_FIXED_SIZE),
-                               rr->rdlength - (len + RRSIG_FIXED_SIZE));
+        length += baseEncode(buffer + length, RemSpc, (const mDNSu8 *)(rd->data + len + RRSIG_FIXED_SIZE),
+                               rr->rdlength - (len + RRSIG_FIXED_SIZE), ENC_BASE64);
     }
     break;
     case kDNSType_DNSKEY:   {
         rdataDNSKey *rrkey = (rdataDNSKey *)rd->data;
     }
     break;
     case kDNSType_DNSKEY:   {
         rdataDNSKey *rrkey = (rdataDNSKey *)rd->data;
-        length += mDNS_snprintf(buffer+length, RemSpc, "\t%d  %d  %s  %u", swap16(rrkey->flags), rrkey->proto,
+        length += mDNS_snprintf(buffer+length, RemSpc, "\t%d  %d  %s  %u ", swap16(rrkey->flags), rrkey->proto,
                                 DNSSECAlgName(rrkey->alg), (unsigned int)keytag((mDNSu8 *)rrkey, rr->rdlength));
                                 DNSSECAlgName(rrkey->alg), (unsigned int)keytag((mDNSu8 *)rrkey, rr->rdlength));
-        length += base64Encode(buffer + length, RemSpc, (mDNSu8 *)(rd->data + DNSKEY_FIXED_SIZE),
-                               rr->rdlength - DNSKEY_FIXED_SIZE);
+        length += baseEncode(buffer + length, RemSpc, (const mDNSu8 *)(rd->data + DNSKEY_FIXED_SIZE),
+                               rr->rdlength - DNSKEY_FIXED_SIZE, ENC_BASE64);
     }
     break;
     case kDNSType_DS:       {
     }
     break;
     case kDNSType_DS:       {
@@ -885,7 +993,7 @@ mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn,
                 const mDNSu8 *s2 = s1 + 1 + s1[0];
                 if (s2[0] && s2[0] < 0x40 && s2[1+s2[0]] == 0)  // and we have three and only three labels
                 {
                 const mDNSu8 *s2 = s1 + 1 + s1[0];
                 if (s2[0] && s2[0] < 0x40 && s2[1+s2[0]] == 0)  // and we have three and only three labels
                 {
-                    static const mDNSu8 SubTypeLabel[5] = "\x04_sub";
+                    static const mDNSu8 SubTypeLabel[5] = mDNSSubTypeLabel;
                     src = s0;                                   // Copy the first label
                     len = *src;
                     for (i=0; i <= len;                      i++) *dst++ = *src++;
                     src = s0;                                   // Copy the first label
                     len = *src;
                     for (i=0; i <= len;                      i++) *dst++ = *src++;
@@ -1022,6 +1130,83 @@ mDNSexport mDNSBool DeconstructServiceName(const domainname *const fqdn,
     return(mDNStrue);
 }
 
     return(mDNStrue);
 }
 
+mDNSexport mStatus DNSNameToLowerCase(domainname *d, domainname *result)
+{
+    const mDNSu8 *a = d->c;
+    mDNSu8 *b = result->c;
+    const mDNSu8 *const max = d->c + MAX_DOMAIN_NAME;
+    int i, len;
+
+    while (*a)
+    {
+        if (a + 1 + *a >= max)
+        {
+            LogMsg("DNSNameToLowerCase: ERROR!! Malformed Domain name");
+            return mStatus_BadParamErr;
+        }
+        len = *a++;
+        *b++ = len;
+        for (i = 0; i < len; i++)
+        {
+            mDNSu8 ac = *a++;
+            if (mDNSIsUpperCase(ac)) ac += 'a' - 'A';
+            *b++ = ac;
+        }
+    }
+    *b = 0;
+
+    return mStatus_NoError;
+}
+
+mDNSexport const mDNSu8 *NSEC3HashName(const domainname *name, rdataNSEC3 *nsec3, const mDNSu8 *AnonData, int AnonDataLen,
+    const mDNSu8 hash[NSEC3_MAX_HASH_LEN], int *dlen)
+{
+    AlgContext *ctx;
+    int i;
+    domainname lname;
+    mDNSu8 *p = (mDNSu8 *)&nsec3->salt;
+    const mDNSu8 *digest;
+    int digestlen;
+    mDNSBool first = mDNStrue;
+
+    if (DNSNameToLowerCase((domainname *)name, &lname) != mStatus_NoError)
+    {
+        LogMsg("NSEC3HashName: ERROR!! DNSNameToLowerCase failed");
+        return mDNSNULL;
+    }
+
+    digest = lname.c;
+    digestlen = DomainNameLength(&lname);
+
+    // Note that it is "i <=". The first iteration is for digesting the name and salt.
+    // The iteration count does not include that.
+    for (i = 0; i <= swap16(nsec3->iterations); i++)
+    {
+        ctx = AlgCreate(DIGEST_ALG, nsec3->alg);
+        if (!ctx)
+        {
+            LogMsg("NSEC3HashName: ERROR!! Cannot allocate context");
+            return mDNSNULL;
+        }
+
+        AlgAdd(ctx, digest, digestlen);
+        if (nsec3->saltLength)
+            AlgAdd(ctx, p, nsec3->saltLength);
+        if (AnonDataLen)
+            AlgAdd(ctx, AnonData, AnonDataLen);
+        if (first)
+        {
+            first = mDNSfalse;
+            digest = hash;
+            digestlen = AlgLength(ctx);
+        }
+        AlgFinal(ctx, (void *)digest, digestlen);
+        AlgDestroy(ctx);
+    }
+    *dlen = digestlen;
+    return digest;
+}
+
 // Notes on UTF-8:
 // 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F
 // 10xxxxxx is a continuation byte of a multi-byte character
 // Notes on UTF-8:
 // 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F
 // 10xxxxxx is a continuation byte of a multi-byte character
@@ -1206,6 +1391,7 @@ mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mD
     rr->resrec.rrclass           = kDNSClass_IN;
     rr->resrec.rroriginalttl     = ttl;
     rr->resrec.rDNSServer        = mDNSNULL;
     rr->resrec.rrclass           = kDNSClass_IN;
     rr->resrec.rroriginalttl     = ttl;
     rr->resrec.rDNSServer        = mDNSNULL;
+    rr->resrec.AnonInfo          = mDNSNULL;
 //     rr->resrec.rdlength          = MUST set by client and/or in mDNS_Register_internal
 //     rr->resrec.rdestimate        = set in mDNS_Register_internal
 //     rr->resrec.rdata             = MUST be set by client
 //     rr->resrec.rdlength          = MUST set by client and/or in mDNS_Register_internal
 //     rr->resrec.rdestimate        = set in mDNS_Register_internal
 //     rr->resrec.rdata             = MUST be set by client
@@ -1235,6 +1421,7 @@ mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mD
     rr->TimeRcvd          = 0;
     rr->TimeExpire        = 0;
     rr->ARType            = artype;
     rr->TimeRcvd          = 0;
     rr->TimeExpire        = 0;
     rr->ARType            = artype;
+    rr->AuthFlags         = 0;
 
     // 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)
 
     // 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)
@@ -1282,10 +1469,15 @@ mDNSexport void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID I
     q->RetryWithSearchDomains = mDNSfalse;
     q->TimeoutQuestion     = 0;
     q->WakeOnResolve       = 0;
     q->RetryWithSearchDomains = mDNSfalse;
     q->TimeoutQuestion     = 0;
     q->WakeOnResolve       = 0;
-    q->UseBrackgroundTrafficClass = mDNSfalse;
+    q->UseBackgroundTrafficClass = mDNSfalse;
     q->ValidationRequired  = 0;
     q->ValidatingResponse  = 0;
     q->ValidationRequired  = 0;
     q->ValidatingResponse  = 0;
+    q->ProxyQuestion       = 0;
     q->qnameOrig           = mDNSNULL;
     q->qnameOrig           = mDNSNULL;
+    q->AnonInfo            = mDNSNULL;
+    q->pid                 = mDNSPlatformGetPID();
+    q->DisallowPID         = mDNSfalse;
+    q->ServiceID           = -1;
     q->QuestionCallback    = callback;
     q->QuestionContext     = context;
 }
     q->QuestionCallback    = callback;
     q->QuestionContext     = context;
 }
@@ -1425,32 +1617,20 @@ mDNSexport mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBod
     }
 }
 
     }
 }
 
-// Don't call this function if the resource record is not NSEC. It will return false
-// which means that the type does not exist.
-mDNSexport mDNSBool RRAssertsExistence(const ResourceRecord *const rr, mDNSu16 type)
+mDNSexport mDNSBool BitmapTypeCheck(mDNSu8 *bmap, int bitmaplen, mDNSu16 type)
 {
 {
-    const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
-    mDNSu8 *nsec = (mDNSu8 *)rdb->data;
-    int len, bitmaplen;
     int win, wlen;
     int win, wlen;
-    mDNSu8 *bmap;
     int wintype;
 
     int wintype;
 
-    if (rr->rrtype != kDNSType_NSEC) return mDNSfalse;
-
-    len = DomainNameLength((domainname *)nsec);
-
     // The window that this type belongs to. NSEC has 256 windows that
     // comprises of 256 types.
     wintype = type >> 8;
 
     // The window that this type belongs to. NSEC has 256 windows that
     // comprises of 256 types.
     wintype = type >> 8;
 
-    bitmaplen = rr->rdlength - len;
-    bmap = nsec + len;
     while (bitmaplen > 0)
     {
         if (bitmaplen < 3)
         {
     while (bitmaplen > 0)
     {
         if (bitmaplen < 3)
         {
-            LogInfo("RRAssertsExistence: malformed nsec, bitmaplen %d short", bitmaplen);
+            LogInfo("BitmapTypeCheck: malformed nsec, bitmaplen %d short", bitmaplen);
             return mDNSfalse;
         }
 
             return mDNSfalse;
         }
 
@@ -1459,12 +1639,12 @@ mDNSexport mDNSBool RRAssertsExistence(const ResourceRecord *const rr, mDNSu16 t
         bitmaplen -= 2;
         if (bitmaplen < wlen || wlen < 1 || wlen > 32)
         {
         bitmaplen -= 2;
         if (bitmaplen < wlen || wlen < 1 || wlen > 32)
         {
-            LogInfo("RRAssertsExistence: malformed nsec, bitmaplen %d wlen %d, win %d", bitmaplen, wlen, win);
+            LogInfo("BitmapTypeCheck: malformed nsec, bitmaplen %d wlen %d, win %d", bitmaplen, wlen, win);
             return mDNSfalse;
         }
         if (win < 0 || win >= 256)
         {
             return mDNSfalse;
         }
         if (win < 0 || win >= 256)
         {
-            LogInfo("RRAssertsExistence: malformed nsec, wlen %d", wlen);
+            LogInfo("BitmapTypeCheck: malformed nsec, wlen %d", wlen);
             return mDNSfalse;
         }
         if (win == wintype)
             return mDNSfalse;
         }
         if (win == wintype)
@@ -1489,6 +1669,24 @@ mDNSexport mDNSBool RRAssertsExistence(const ResourceRecord *const rr, mDNSu16 t
     return mDNSfalse;
 }
 
     return mDNSfalse;
 }
 
+// Don't call this function if the resource record is not NSEC. It will return false
+// which means that the type does not exist.
+mDNSexport mDNSBool RRAssertsExistence(const ResourceRecord *const rr, mDNSu16 type)
+{
+    const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
+    mDNSu8 *nsec = (mDNSu8 *)rdb->data;
+    int len, bitmaplen;
+    mDNSu8 *bmap;
+
+    if (rr->rrtype != kDNSType_NSEC) return mDNSfalse;
+
+    len = DomainNameLength((domainname *)nsec);
+
+    bitmaplen = rr->rdlength - len;
+    bmap = nsec + len;
+    return (BitmapTypeCheck(bmap, bitmaplen, type));
+}
+
 // Don't call this function if the resource record is not NSEC. It will return false
 // which means that the type exists.
 mDNSexport mDNSBool RRAssertsNonexistence(const ResourceRecord *const rr, mDNSu16 type)
 // Don't call this function if the resource record is not NSEC. It will return false
 // which means that the type exists.
 mDNSexport mDNSBool RRAssertsNonexistence(const ResourceRecord *const rr, mDNSu16 type)
@@ -1509,6 +1707,22 @@ mDNSlocal mDNSBool DNSSECRecordAnswersQuestion(const ResourceRecord *const rr, c
     if (q->qtype == rr->rrtype)
         return mDNStrue;
 
     if (q->qtype == rr->rrtype)
         return mDNStrue;
 
+    // For DS and DNSKEY questions, the types should match i.e., don't answer using CNAME
+    // records as it answers any question type.
+    //
+    // - DS record comes from the parent zone where CNAME record cannot coexist and hence
+    //  cannot possibly answer it.
+    //
+    // - For DNSKEY, one could potentially follow CNAME but there could be a DNSKEY at
+    //   the "qname" itself. To keep it simple, we don't follow CNAME.
+
+    if ((q->qtype == kDNSType_DS || q->qtype == kDNSType_DNSKEY) && (q->qtype != rr->rrtype))
+    {
+        debugf("DNSSECRecordAnswersQuestion: %d type resource record matched question %##s (%s), ignoring", rr->rrtype,
+            q->qname.c, DNSTypeName(q->qtype));
+        return mDNSfalse;
+    }
+
     // If we are validating a response using DNSSEC, we might already have the records
     // for the "q->qtype" in the cache but we issued a query with DO bit set
     // to get the RRSIGs e.g., if you have two questions one of which does not require
     // If we are validating a response using DNSSEC, we might already have the records
     // for the "q->qtype" in the cache but we issued a query with DO bit set
     // to get the RRSIGs e.g., if you have two questions one of which does not require
@@ -1519,14 +1733,16 @@ mDNSlocal mDNSBool DNSSECRecordAnswersQuestion(const ResourceRecord *const rr, c
     {
         const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
         rdataRRSig *rrsig = (rdataRRSig *)rdb->data;
     {
         const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
         rdataRRSig *rrsig = (rdataRRSig *)rdb->data;
-        if (q->qtype == kDNSType_CNAME || swap16(rrsig->typeCovered) != q->qtype)
+        mDNSu16 typeCovered = swap16(rrsig->typeCovered);
+        debugf("DNSSECRecordAnswersQuestion: Matching RRSIG typeCovered %s", DNSTypeName(typeCovered));
+        if (typeCovered != kDNSType_CNAME && typeCovered != q->qtype)
         {
         {
-            debugf("DNSSECRecordAnswersQuestion: Question %##s (%s) did not match record %##s (RRSIG)", q->qname.c,
-                    DNSTypeName(q->qtype), rr->name->c);
+            debugf("DNSSECRecordAnswersQuestion: RRSIG did not match question %##s (%s)", q->qname.c,
+                    DNSTypeName(q->qtype));
             return mDNSfalse;
         }
             return mDNSfalse;
         }
-        LogInfo("DNSSECRecordAnswersQuestion: Question %##s (%s) matched record %##s (RRSIG)", q->qname.c,
-                DNSTypeName(q->qtype), rr->name->c);
+        LogInfo("DNSSECRecordAnswersQuestion: RRSIG matched question %##s (%s)", q->qname.c,
+                DNSTypeName(q->qtype));
         *checkType = mDNSfalse;
         return mDNStrue;
     }
         *checkType = mDNSfalse;
         return mDNStrue;
     }
@@ -1581,6 +1797,11 @@ mDNSexport mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr
     // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
     if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
 
     // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
     if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
 
+    // CNAME answers question of any type and a negative cache record should not prevent us from querying other
+    // valid types at the same name.
+    if (rr->rrtype == kDNSType_CNAME && rr->RecordType == kDNSRecordTypePacketNegative && rr->rrtype != q->qtype)
+        return mDNSfalse;
+
     // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
     if (checkType && !RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse);
     if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
     // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
     if (checkType && !RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse);
     if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
@@ -1590,6 +1811,9 @@ mDNSexport mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr
         return mDNSfalse;
 #endif // APPLE_OSX_mDNSResponder
 
         return mDNSfalse;
 #endif // APPLE_OSX_mDNSResponder
 
+    if (!AnonInfoAnswersQuestion(rr, q))
+        return mDNSfalse;
+
     return(mDNStrue);
 }
 
     return(mDNStrue);
 }
 
@@ -1674,6 +1898,9 @@ mDNSexport mDNSBool LocalOnlyRecordAnswersQuestion(AuthRecord *const ar, const D
     if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse);
     if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
 
     if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse);
     if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
 
+    if (!AnonInfoAnswersQuestion(rr, q))
+        return mDNSfalse;
+
     return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
 }
 
     return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
 }
 
@@ -1705,6 +1932,9 @@ mDNSexport mDNSBool AnyTypeRecordAnswersQuestion(const ResourceRecord *const rr,
 
     if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
 
 
     if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
 
+    if (!AnonInfoAnswersQuestion(rr, q))
+        return mDNSfalse;
+
     return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
 }
 
     return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
 }
 
@@ -2069,9 +2299,13 @@ mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNS
         int len = 0;
         const rdataOPT *opt;
         const rdataOPT *const end = (const rdataOPT *)&rr->rdata->u.data[rr->rdlength];
         int len = 0;
         const rdataOPT *opt;
         const rdataOPT *const end = (const rdataOPT *)&rr->rdata->u.data[rr->rdlength];
-        for (opt = &rr->rdata->u.opt[0]; opt < end; opt++) len += DNSOpt_Data_Space(opt);
-        if (ptr + len > limit) { LogMsg("ERROR: putOptRData - out of space"); return mDNSNULL; }
-
+        for (opt = &rr->rdata->u.opt[0]; opt < end; opt++) 
+            len += DNSOpt_Data_Space(opt);
+        if (ptr + len > limit) 
+        { 
+            LogMsg("ERROR: putOptRData - out of space"); 
+            return mDNSNULL; 
+        }
         for (opt = &rr->rdata->u.opt[0]; opt < end; opt++)
         {
             const int space = DNSOpt_Data_Space(opt);
         for (opt = &rr->rdata->u.opt[0]; opt < end; opt++)
         {
             const int space = DNSOpt_Data_Space(opt);
@@ -2106,6 +2340,10 @@ mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNS
                     }
                 }
                 break;
                     }
                 }
                 break;
+            case kDNSOpt_Trace:
+                *ptr++ = opt->u.tracer.platf;
+                ptr    = putVal16(ptr, opt->u.tracer.mDNSv);
+                break;
             }
         }
         return ptr;
             }
         }
         return ptr;
@@ -2207,14 +2445,24 @@ mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *
 
     if (rr->RecordType == kDNSRecordTypeUnregistered)
     {
 
     if (rr->RecordType == kDNSRecordTypeUnregistered)
     {
-        LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype));
+        LogMsg("PutResourceRecordTTLWithLimit ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype));
         return(ptr);
     }
 
         return(ptr);
     }
 
-    if (!ptr) { LogMsg("PutResourceRecordTTLWithLimit ptr is null"); return(mDNSNULL); }
+    if (!ptr)
+    {
+        LogMsg("PutResourceRecordTTLWithLimit ptr is null %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype));
+        return(mDNSNULL);
+    }
 
     ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name);
 
     ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name);
-    if (!ptr || ptr + 10 >= limit) return(mDNSNULL);    // If we're out-of-space, return mDNSNULL
+    // If we're out-of-space, return mDNSNULL
+    if (!ptr || ptr + 10 >= limit)
+    {
+        LogInfo("PutResourceRecordTTLWithLimit: can't put name, out of space %##s (%s), ptr %p, limit %p", rr->name->c,
+            DNSTypeName(rr->rrtype), ptr, limit);
+        return(mDNSNULL);
+    }
     ptr[0] = (mDNSu8)(rr->rrtype  >> 8);
     ptr[1] = (mDNSu8)(rr->rrtype  &  0xFF);
     ptr[2] = (mDNSu8)(rr->rrclass >> 8);
     ptr[0] = (mDNSu8)(rr->rrtype  >> 8);
     ptr[1] = (mDNSu8)(rr->rrtype  &  0xFF);
     ptr[2] = (mDNSu8)(rr->rrclass >> 8);
@@ -2226,7 +2474,12 @@ mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *
     // ptr[8] and ptr[9] filled in *after* we find out how much space the rdata takes
 
     endofrdata = putRData(rdatacompressionbase, ptr+10, limit, rr);
     // ptr[8] and ptr[9] filled in *after* we find out how much space the rdata takes
 
     endofrdata = putRData(rdatacompressionbase, ptr+10, limit, rr);
-    if (!endofrdata) { verbosedebugf("Ran out of space in PutResourceRecord for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); return(mDNSNULL); }
+    if (!endofrdata)
+    {
+        LogInfo("PutResourceRecordTTLWithLimit: Ran out of space in PutResourceRecord for %##s (%s), ptr %p, limit %p", rr->name->c,
+            DNSTypeName(rr->rrtype), ptr+10, limit);
+        return(mDNSNULL);
+    }
 
     // Go back and fill in the actual number of data bytes we wrote
     // (actualLength can be less than rdlength when domain name compression is used)
 
     // Go back and fill in the actual number of data bytes we wrote
     // (actualLength can be less than rdlength when domain name compression is used)
@@ -2550,6 +2803,39 @@ mDNSexport const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8
     return(ptr + pktrdlength);
 }
 
     return(ptr + pktrdlength);
 }
 
+// Sanity check whether the NSEC/NSEC3 bitmap is good
+mDNSlocal mDNSu8 *SanityCheckBitMap(const mDNSu8 *bmap, const mDNSu8 *end, int len)
+{
+    int win, wlen;
+
+    while (bmap < end)
+    {
+        if (len < 3)
+        {
+            LogInfo("SanityCheckBitMap: invalid length %d", len);
+            return mDNSNULL;
+        }
+
+        win = *bmap++;
+        wlen = *bmap++;
+        len -= 2;
+        if (len < wlen || wlen < 1 || wlen > 32)
+        {
+            LogInfo("SanityCheckBitMap: invalid window length %d", wlen);
+            return mDNSNULL;
+        }
+        if (win < 0 || win >= 256)
+        {
+            LogInfo("SanityCheckBitMap: invalid window %d", win);
+            return mDNSNULL;
+        }
+
+        bmap += wlen;
+        len -= wlen;
+    }
+    return (mDNSu8 *)bmap;
+}
+
 // This function is called with "msg" when we receive a DNS message and needs to parse a single resource record
 // pointed to by "ptr". Some resource records like SOA, SRV are converted to host order and also expanded
 // (domainnames are expanded to 255 bytes) when stored in memory.
 // This function is called with "msg" when we receive a DNS message and needs to parse a single resource record
 // pointed to by "ptr". Some resource records like SOA, SRV are converted to host order and also expanded
 // (domainnames are expanded to 255 bytes) when stored in memory.
@@ -2782,7 +3068,6 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con
         int savelen, len;
         domainname name;
         const mDNSu8 *orig = ptr;
         int savelen, len;
         domainname name;
         const mDNSu8 *orig = ptr;
-        const mDNSu8 *save;
 
         // Make sure the data is parseable and within the limits. DNSSEC code looks at
         // the domain name in the end for a valid domainname.
 
         // Make sure the data is parseable and within the limits. DNSSEC code looks at
         // the domain name in the end for a valid domainname.
@@ -2822,7 +3107,6 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con
             goto fail;
         }
 
             goto fail;
         }
 
-        save = ptr;
         savelen = ptr - orig;
 
         // RFC 2915 states that name compression is not allowed for this field. But RFC 3597
         savelen = ptr - orig;
 
         // RFC 2915 states that name compression is not allowed for this field. But RFC 3597
@@ -2910,6 +3194,14 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con
                     opt++;
                 }
                 break;
                     opt++;
                 }
                 break;
+            case kDNSOpt_Trace:
+                if (opt->optlen == DNSOpt_TraceData_Space - 4)
+                {
+                    opt->u.tracer.platf  = ptr[0];
+                    opt->u.tracer.mDNSv  = (mDNSu16)((mDNSu16)ptr[1] <<  8 | ptr[2]);
+                    opt++;
+                }
+                break;
             }
             ptr += currentopt->optlen;
         }
             }
             ptr += currentopt->optlen;
         }
@@ -2920,7 +3212,6 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con
 
     case kDNSType_NSEC: {
         domainname name;
 
     case kDNSType_NSEC: {
         domainname name;
-        int win, wlen;
         int len = rdlength;
         int bmaplen, dlen;
         const mDNSu8 *orig = ptr;
         int len = rdlength;
         int bmaplen, dlen;
         const mDNSu8 *orig = ptr;
@@ -2950,32 +3241,9 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con
         len -= (ptr - orig);
         bmaplen = len;                  // Save the length of the bitmap
         bmap = ptr;
         len -= (ptr - orig);
         bmaplen = len;                  // Save the length of the bitmap
         bmap = ptr;
-        // Sanity check whether the bitmap is good
-        while (ptr < end)
-        {
-            if (len < 3)
-            {
-                LogInfo("SetRData: invalid length %d", len);
-                goto fail;
-            }
-
-            win = *ptr++;
-            wlen = *ptr++;
-            len -= 2;
-            if (len < wlen || wlen < 1 || wlen > 32)
-            {
-                LogInfo("SetRData: invalid window length %d", wlen);
-                goto fail;
-            }
-            if (win < 0 || win >= 256)
-            {
-                LogInfo("SetRData: invalid window %d", win);
-                goto fail;
-            }
-
-            ptr += wlen;
-            len -= wlen;
-        }
+        ptr = SanityCheckBitMap(bmap, end, len);
+        if (!ptr)
+            goto fail;
         if (ptr != end)
         {
             LogInfo("SetRData: Malformed NSEC length not right");
         if (ptr != end)
         {
             LogInfo("SetRData: Malformed NSEC length not right");
@@ -2997,6 +3265,56 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con
         mDNSPlatformMemCopy(rdb->data + dlen, bmap, bmaplen);
         break;
     }
         mDNSPlatformMemCopy(rdb->data + dlen, bmap, bmaplen);
         break;
     }
+    case kDNSType_NSEC3:
+    {
+        rdataNSEC3 *nsec3 = (rdataNSEC3 *)ptr;
+        mDNSu8 *p = (mDNSu8 *)&nsec3->salt;
+        int hashLength, bitmaplen;
+
+        if (rdlength < NSEC3_FIXED_SIZE + 1)
+        {
+            LogInfo("SetRData: NSEC3 too small length %d", rdlength);
+            goto fail;
+        }
+        if (nsec3->alg != SHA1_DIGEST_TYPE)
+        {
+            LogInfo("SetRData: nsec3 alg %d not supported", nsec3->alg);
+            goto fail;
+        }
+        if (swap16(nsec3->iterations) > NSEC3_MAX_ITERATIONS)
+        {
+            LogInfo("SetRData: nsec3 iteration count %d too big", swap16(nsec3->iterations));
+            goto fail;
+        } 
+        p += nsec3->saltLength;
+        // There should at least be one byte beyond saltLength
+        if (p >= end)
+        {
+            LogInfo("SetRData: nsec3 too small, at saltlength %d, p %p, end %p", nsec3->saltLength, p, end);
+            goto fail;
+        }
+        // p is pointing at hashLength
+        hashLength = (int)*p++;
+        if (!hashLength)
+        {
+            LogInfo("SetRData: hashLength zero");
+            goto fail;
+        }
+        p += hashLength;
+        if (p > end)
+        {
+            LogInfo("SetRData: nsec3 too small, at hashLength %d, p %p, end %p", hashLength, p, end);
+            goto fail;
+        }
+
+        bitmaplen = rdlength - (int)(p - ptr);
+        p = SanityCheckBitMap(p, end, bitmaplen);
+        if (!p)
+            goto fail;
+        rr->resrec.rdlength = rdlength;
+        mDNSPlatformMemCopy(rdb->data, ptr, rdlength);
+        break;
+    }
     case kDNSType_TKEY:
     case kDNSType_TSIG:
     {
     case kDNSType_TKEY:
     case kDNSType_TSIG:
     {
@@ -3088,7 +3406,6 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con
         mDNSPlatformMemCopy(rdb->data, ptr, rdlength);
         break;
     }
         mDNSPlatformMemCopy(rdb->data, ptr, rdlength);
         break;
     }
-
     default:
         debugf("SetRData: Warning! Reading resource type %d (%s) as opaque data",
                rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype));
     default:
         debugf("SetRData: Warning! Reading resource type %d (%s) as opaque data",
                rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype));
@@ -3352,7 +3669,7 @@ mDNSexport void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *t
     DNSQuestion q;
     char tbuffer[64], sbuffer[64], dbuffer[64] = "";
     if (!status) tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), sent ? "Sent" : "Received"                        )] = 0;
     DNSQuestion q;
     char tbuffer[64], sbuffer[64], dbuffer[64] = "";
     if (!status) tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), sent ? "Sent" : "Received"                        )] = 0;
-    else tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), "ERROR %d %sing", status, sent ? "Send" : "Receiv")] = 0;
+    else tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), "ERROR %d %sing", status, sent ? "Send" : "Receive")] = 0;
     if (sent) sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "port "        )] = 0;
     else sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "%#a:", srcaddr)] = 0;
     if (dstaddr || !mDNSIPPortIsZero(dstport))
     if (sent) sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "port "        )] = 0;
     else sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "%#a:", srcaddr)] = 0;
     if (dstaddr || !mDNSIPPortIsZero(dstport))
@@ -3415,6 +3732,14 @@ mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNS
     mDNSu8 *newend;
     mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData;
 
     mDNSu8 *newend;
     mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData;
 
+#if APPLE_OSX_mDNSResponder
+    // maintain outbound packet statistics
+    if (mDNSOpaque16IsZero(msg->h.id))
+        m->MulticastPacketsSent++;
+    else
+        m->UnicastPacketsSent++;
+#endif // APPLE_OSX_mDNSResponder
+
     // Zero-length message data is okay (e.g. for a DNS Update ack, where all we need is an ID and an error code
     if (end < msg->data || end - msg->data > AbsoluteMaxDNSMessageData)
     {
     // Zero-length message data is okay (e.g. for a DNS Update ack, where all we need is an ID and an error code
     if (end < msg->data || end - msg->data > AbsoluteMaxDNSMessageData)
     {
@@ -3440,12 +3765,41 @@ mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNS
         {
             mDNSu16 msglen = (mDNSu16)(end - (mDNSu8 *)msg);
             mDNSu8 lenbuf[2] = { (mDNSu8)(msglen >> 8), (mDNSu8)(msglen & 0xFF) };
         {
             mDNSu16 msglen = (mDNSu16)(end - (mDNSu8 *)msg);
             mDNSu8 lenbuf[2] = { (mDNSu8)(msglen >> 8), (mDNSu8)(msglen & 0xFF) };
-            long nsent = mDNSPlatformWriteTCP(sock, (char*)lenbuf, 2);      // Should do scatter/gather here -- this is probably going out as two packets
-            if (nsent != 2) { LogMsg("mDNSSendDNSMessage: write msg length failed %d/%d", nsent, 2); status = mStatus_ConnFailed; }
+            char *buf;
+            long nsent;
+
+            // Try to send them in one packet if we can allocate enough memory
+            buf = mDNSPlatformMemAllocate(msglen + 2);
+            if (buf)
+            {
+                buf[0] = lenbuf[0];
+                buf[1] = lenbuf[1];
+                mDNSPlatformMemCopy(buf+2, msg, msglen);
+                nsent = mDNSPlatformWriteTCP(sock, buf, msglen+2);
+                if (nsent != (msglen + 2))
+                {
+                    LogMsg("mDNSSendDNSMessage: write message failed %d/%d", nsent, msglen);
+                    status = mStatus_ConnFailed;
+                }
+                mDNSPlatformMemFree(buf);
+            }
             else
             {
             else
             {
-                nsent = mDNSPlatformWriteTCP(sock, (char *)msg, msglen);
-                if (nsent != msglen) { LogMsg("mDNSSendDNSMessage: write msg body failed %d/%d", nsent, msglen); status = mStatus_ConnFailed; }
+                nsent = mDNSPlatformWriteTCP(sock, (char*)lenbuf, 2);
+                if (nsent != 2)
+                {
+                    LogMsg("mDNSSendDNSMessage: write msg length failed %d/%d", nsent, 2);
+                    status = mStatus_ConnFailed;
+                }
+                else
+                {
+                    nsent = mDNSPlatformWriteTCP(sock, (char *)msg, msglen);
+                    if (nsent != msglen)
+                    {
+                        LogMsg("mDNSSendDNSMessage: write msg body failed %d/%d", nsent, msglen);
+                        status = mStatus_ConnFailed;
+                    }
+                }
             }
         }
     }
             }
         }
     }
@@ -3546,7 +3900,6 @@ mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m)
     if (e - m->NextCacheCheck        > 0) e = m->NextCacheCheck;
     if (e - m->NextScheduledSPS      > 0) e = m->NextScheduledSPS;
     if (e - m->NextScheduledKA       > 0) e = m->NextScheduledKA;
     if (e - m->NextCacheCheck        > 0) e = m->NextCacheCheck;
     if (e - m->NextScheduledSPS      > 0) e = m->NextScheduledSPS;
     if (e - m->NextScheduledKA       > 0) e = m->NextScheduledKA;
-    if (m->clearIgnoreNA && (e - m->clearIgnoreNA > 0)) e = m->clearIgnoreNA;
 
     // NextScheduledSPRetry only valid when DelaySleep not set
     if (!m->DelaySleep && m->SleepLimit && e - m->NextScheduledSPRetry > 0) e = m->NextScheduledSPRetry;
 
     // NextScheduledSPRetry only valid when DelaySleep not set
     if (!m->DelaySleep && m->SleepLimit && e - m->NextScheduledSPRetry > 0) e = m->NextScheduledSPRetry;
@@ -3625,8 +3978,7 @@ mDNSexport void ShowTaskSchedulingError(mDNS *const m)
         LogMsg("Task Scheduling Error: m->NextScheduledProbe %d",    m->timenow - m->NextScheduledProbe);
     if (m->timenow - m->NextScheduledResponse >= 0)
         LogMsg("Task Scheduling Error: m->NextScheduledResponse %d", m->timenow - m->NextScheduledResponse);
         LogMsg("Task Scheduling Error: m->NextScheduledProbe %d",    m->timenow - m->NextScheduledProbe);
     if (m->timenow - m->NextScheduledResponse >= 0)
         LogMsg("Task Scheduling Error: m->NextScheduledResponse %d", m->timenow - m->NextScheduledResponse);
-    if (m->clearIgnoreNA && m->timenow - m->clearIgnoreNA >= 0)
-        LogMsg("Task Scheduling Error: m->clearIgnoreNA %d", m->timenow - m->clearIgnoreNA);
+
     mDNS_Unlock(m);
 }
 
     mDNS_Unlock(m);
 }
 
index fe8ccba6101435e3f45ad2c130dedba4796b61a3..b92f5a9ab4752bad42694bc5bb00535c9767f75c 100644 (file)
@@ -101,6 +101,8 @@ extern mDNSu32 mDNSRandom(mDNSu32 max);     // Returns pseudo-random result from
 #pragma mark - Domain Name Utility Functions
 #endif
 
 #pragma mark - Domain Name Utility Functions
 #endif
 
+#define mDNSSubTypeLabel   "\x04_sub"
+
 #define mDNSIsDigit(X)     ((X) >= '0' && (X) <= '9')
 #define mDNSIsUpperCase(X) ((X) >= 'A' && (X) <= 'Z')
 #define mDNSIsLowerCase(X) ((X) >= 'a' && (X) <= 'z')
 #define mDNSIsDigit(X)     ((X) >= '0' && (X) <= '9')
 #define mDNSIsUpperCase(X) ((X) >= 'A' && (X) <= 'Z')
 #define mDNSIsLowerCase(X) ((X) >= 'a' && (X) <= 'z')
@@ -169,6 +171,7 @@ extern mDNSBool ResourceRecordAnswersUnicastResponse(const ResourceRecord *const
 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);
 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);
+extern mStatus DNSNameToLowerCase(domainname *d, domainname *result);
 
 #define GetRRDomainNameTarget(RR) (                                                                          \
         ((RR)->rrtype == kDNSType_NS || (RR)->rrtype == kDNSType_CNAME || (RR)->rrtype == kDNSType_PTR || (RR)->rrtype == kDNSType_DNAME) ? &(RR)->rdata->u.name        : \
 
 #define GetRRDomainNameTarget(RR) (                                                                          \
         ((RR)->rrtype == kDNSType_NS || (RR)->rrtype == kDNSType_CNAME || (RR)->rrtype == kDNSType_PTR || (RR)->rrtype == kDNSType_DNAME) ? &(RR)->rdata->u.name        : \
@@ -204,9 +207,9 @@ extern mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr,
 #define PutResourceRecord(MSG, P, C, RR) PutResourceRecordTTL((MSG), (P), (C), (RR), (RR)->rroriginalttl)
 
 // The PutRR_OS variants assume a local variable 'm', put build the packet at m->omsg,
 #define PutResourceRecord(MSG, P, C, RR) PutResourceRecordTTL((MSG), (P), (C), (RR), (RR)->rroriginalttl)
 
 // The PutRR_OS variants assume a local variable 'm', put build the packet at m->omsg,
-// and assume a local variable 'OwnerRecordSpace' indicating how many bytes (if any) to reserve to add an OWNER option at the end
+// and assume local variables 'OwnerRecordSpace' & 'TraceRecordSpace' indicating how many bytes (if any) to reserve to add an OWNER/TRACER option at the end
 #define PutRR_OS_TTL(ptr, count, rr, ttl) \
 #define PutRR_OS_TTL(ptr, count, rr, ttl) \
-    PutResourceRecordTTLWithLimit(&m->omsg, (ptr), (count), (rr), (ttl), m->omsg.data + AllowedRRSpace(&m->omsg) - OwnerRecordSpace)
+    PutResourceRecordTTLWithLimit(&m->omsg, (ptr), (count), (rr), (ttl), m->omsg.data + AllowedRRSpace(&m->omsg) - OwnerRecordSpace - TraceRecordSpace)
 
 #define PutRR_OS(P, C, RR) PutRR_OS_TTL((P), (C), (RR), (RR)->rroriginalttl)
 
 
 #define PutRR_OS(P, C, RR) PutRR_OS_TTL((P), (C), (RR), (RR)->rroriginalttl)
 
@@ -221,7 +224,12 @@ extern mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease);
 extern mDNSu8 *putUpdateLeaseWithLimit(DNSMessage *msg, mDNSu8 *ptr, mDNSu32 lease, mDNSu8 *limit);
 
 extern mDNSu8 *putHINFO(const mDNS *const m, DNSMessage *const msg, mDNSu8 *ptr, DomainAuthInfo *authInfo, mDNSu8 *limit);
 extern mDNSu8 *putUpdateLeaseWithLimit(DNSMessage *msg, mDNSu8 *ptr, mDNSu32 lease, mDNSu8 *limit);
 
 extern mDNSu8 *putHINFO(const mDNS *const m, DNSMessage *const msg, mDNSu8 *ptr, DomainAuthInfo *authInfo, mDNSu8 *limit);
-mDNSexport mDNSu8 *putDNSSECOption(DNSMessage *msg, mDNSu8 *end, mDNSu8 *limit);
+extern mDNSu8 *putDNSSECOption(DNSMessage *msg, mDNSu8 *end, mDNSu8 *limit);
+extern int baseEncode(char *buffer, int blen, const mDNSu8 *data, int len, int encAlg);
+extern void NSEC3Parse(const ResourceRecord *const rr, mDNSu8 **salt, int *hashLength, mDNSu8 **nxtName, int *bitmaplen, mDNSu8 **bitmap);
+
+extern const mDNSu8 *NSEC3HashName(const domainname *name, rdataNSEC3 *nsec3, const mDNSu8 *AnonData, int AnonDataLen,
+    const mDNSu8 hash[NSEC3_MAX_HASH_LEN], int *dlen);
 
 // ***************************************************************************
 #if COMPILER_LIKES_PRAGMA_MARK
 
 // ***************************************************************************
 #if COMPILER_LIKES_PRAGMA_MARK
@@ -255,6 +263,7 @@ extern void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *trans
                        const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end);
 extern mDNSBool RRAssertsNonexistence(const ResourceRecord *const rr, mDNSu16 type);
 extern mDNSBool RRAssertsExistence(const ResourceRecord *const rr, mDNSu16 type);
                        const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end);
 extern mDNSBool RRAssertsNonexistence(const ResourceRecord *const rr, mDNSu16 type);
 extern mDNSBool RRAssertsExistence(const ResourceRecord *const rr, mDNSu16 type);
+extern mDNSBool BitmapTypeCheck(mDNSu8 *bmap, int bitmaplen, mDNSu16 type);
 
 extern mDNSu16 swap16(mDNSu16 x);
 extern mDNSu32 swap32(mDNSu32 x);
 
 extern mDNSu16 swap16(mDNSu16 x);
 extern mDNSu32 swap32(mDNSu32 x);
index 1ad8d0cae90b1e7dcf87ae8309b6607b75073f03..33798d383373cf0ee7a16cdfb98b42761be1c8ef 100644 (file)
@@ -1426,7 +1426,6 @@ mDNSexport mDNSBool DNSDigest_VerifyMessage(DNSMessage *msg, mDNSu8 *end, LargeC
     mDNSs32 then;
     mDNSu8 thisDigest[MD5_LEN];
     mDNSu8 thatDigest[MD5_LEN];
     mDNSs32 then;
     mDNSu8 thisDigest[MD5_LEN];
     mDNSu8 thatDigest[MD5_LEN];
-    mDNSu32 macsize;
     mDNSOpaque16 buf;
     mDNSu8 utc48[6];
     mDNSs32 delta;
     mDNSOpaque16 buf;
     mDNSu8 utc48[6];
     mDNSs32 delta;
@@ -1490,8 +1489,6 @@ mDNSexport mDNSBool DNSDigest_VerifyMessage(DNSMessage *msg, mDNSu8 *end, LargeC
 
     // MAC size
 
 
     // MAC size
 
-    macsize = (mDNSu32) NToH16(ptr);
-
     ptr += sizeof(mDNSu16);
 
     // MAC
     ptr += sizeof(mDNSu16);
 
     // MAC
diff --git a/mDNSCore/anonymous.c b/mDNSCore/anonymous.c
new file mode 100644 (file)
index 0000000..94b102e
--- /dev/null
@@ -0,0 +1,597 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2012 Apple Computer, 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 "mDNSEmbeddedAPI.h"
+#include "CryptoAlg.h"
+#include "anonymous.h"
+#include "DNSCommon.h"
+
+// Define ANONYMOUS_DISABLED to remove all the anonymous functionality
+// and use the stub functions implemented later in this file.
+
+#ifndef ANONYMOUS_DISABLED
+
+#define ANON_NSEC3_ITERATIONS        1 
+
+mDNSlocal mDNSBool InitializeNSEC3Record(ResourceRecord *rr, const mDNSu8 *AnonData, int len, mDNSu32 salt)
+{
+    const mDNSu8 *ptr;
+    rdataNSEC3 *nsec3 = (rdataNSEC3 *)rr->rdata->u.data;
+    mDNSu8 *tmp, *nxt;
+    unsigned short iter = ANON_NSEC3_ITERATIONS;
+    int hlen;
+    const mDNSu8 hashName[NSEC3_MAX_HASH_LEN];
+
+    // Construct the RDATA first and construct the owner name based on that.
+    ptr = (const mDNSu8 *)&salt;
+    debugf("InitializeNSEC3Record: %x%x%x%x, name %##s", ptr[0], ptr[1], ptr[2], ptr[3], rr->name->c);
+
+    // Set the RDATA
+    nsec3->alg = SHA1_DIGEST_TYPE;
+    nsec3->flags = 0;
+    nsec3->iterations = swap16(iter);
+    nsec3->saltLength = 4;
+    tmp = (mDNSu8 *)&nsec3->salt;
+    *tmp++ = ptr[0];
+    *tmp++ = ptr[1];
+    *tmp++ = ptr[2];
+    *tmp++ = ptr[3];
+
+    // hashLength, nxt, bitmap
+    *tmp++ = SHA1_HASH_LENGTH;    // hash length
+    nxt = tmp;
+    tmp += SHA1_HASH_LENGTH;
+    *tmp++ = 0; // window number
+    *tmp++ = NSEC_MCAST_WINDOW_SIZE; // window length
+    mDNSPlatformMemZero(tmp, NSEC_MCAST_WINDOW_SIZE);
+    tmp[kDNSType_PTR >> 3] |= 128 >> (kDNSType_PTR & 7);
+
+    // Hash the base service name + salt + AnonData
+    if (!NSEC3HashName(rr->name, nsec3, AnonData, len, hashName, &hlen))
+    {
+        LogMsg("InitializeNSEC3Record: NSEC3HashName failed for ##s", rr->name->c);
+        return mDNSfalse;
+    }
+    if (hlen != SHA1_HASH_LENGTH)
+    {
+        LogMsg("InitializeNSEC3Record: hlen wrong %d", hlen);
+        return mDNSfalse;
+    }
+    mDNSPlatformMemCopy(nxt, hashName, hlen);
+
+    return mDNStrue;
+}
+
+mDNSlocal ResourceRecord *ConstructNSEC3Record(const domainname *service, const mDNSu8 *AnonData, int len, mDNSu32 salt)
+{
+    ResourceRecord *rr;
+    int dlen;
+    domainname *name;
+
+    // We are just allocating an RData which has StandardAuthRDSize
+    if (StandardAuthRDSize < MCAST_NSEC3_RDLENGTH)
+    {
+        LogMsg("ConstructNSEC3Record: StandardAuthRDSize %d smaller than MCAST_NSEC3_RDLENGTH %d", StandardAuthRDSize, MCAST_NSEC3_RDLENGTH);
+        return mDNSNULL;
+    }
+
+    dlen = DomainNameLength(service);
+    // Allocate space for the name and RData. 
+    rr = mDNSPlatformMemAllocate(sizeof(ResourceRecord) + dlen + sizeof(RData));
+    if (!rr)
+        return mDNSNULL;
+    name = (domainname *)((mDNSu8 *)rr + sizeof(ResourceRecord));
+    rr->RecordType        = kDNSRecordTypePacketAuth;
+    rr->InterfaceID       = mDNSInterface_Any;
+    rr->name              = (const domainname *)name;
+    rr->rrtype            = kDNSType_NSEC3;
+    rr->rrclass           = kDNSClass_IN;
+    rr->rroriginalttl     = kStandardTTL;
+    rr->rDNSServer        = mDNSNULL;
+    rr->rdlength          = MCAST_NSEC3_RDLENGTH;
+    rr->rdestimate        = MCAST_NSEC3_RDLENGTH;
+    rr->rdata             = (RData *)((mDNSu8 *)rr->name + dlen);
+
+    AssignDomainName(name, service);
+    if (!InitializeNSEC3Record(rr, AnonData, len, salt))
+    {
+        mDNSPlatformMemFree(rr);
+        return mDNSNULL;
+    }
+    return rr;
+}
+
+mDNSlocal ResourceRecord *CopyNSEC3ResourceRecord(AnonymousInfo *si, const ResourceRecord *rr)
+{
+    int len;
+    domainname *name;
+    ResourceRecord *nsec3rr;
+
+    if (rr->rdlength < MCAST_NSEC3_RDLENGTH)
+    {
+        LogMsg("CopyNSEC3ResourceRecord: rdlength %d smaller than MCAST_NSEC3_RDLENGTH %d", rr->rdlength, MCAST_NSEC3_RDLENGTH);
+        return mDNSNULL;
+    }
+    // Allocate space for the name and the rdata along with the ResourceRecord
+    len = DomainNameLength(rr->name);
+    nsec3rr = mDNSPlatformMemAllocate(sizeof(ResourceRecord) + len + sizeof(RData));
+    if (!nsec3rr)
+        return mDNSNULL;
+
+    *nsec3rr = *rr;
+    name = (domainname *)((mDNSu8 *)nsec3rr + sizeof(ResourceRecord));
+    nsec3rr->name = (const domainname *)name;
+    AssignDomainName(name, rr->name);
+
+    nsec3rr->rdata = (RData *)((mDNSu8 *)nsec3rr->name + len);
+    mDNSPlatformMemCopy(nsec3rr->rdata->u.data, rr->rdata->u.data, rr->rdlength);
+
+    si->nsec3RR = nsec3rr;
+
+    return nsec3rr;
+}
+
+// When a service is started or a browse is started with the Anonymous data, we allocate a new random
+// number and based on that allocate a new NSEC3 resource record whose hash is a function of random number (salt) and
+// the anonymous data.
+//
+// If we receive a packet with the NSEC3 option, we need to cache that along with the resource record so that we can
+// check against the question to see whether it answers them or not. In that case, we pass the "rr" that we received.
+mDNSexport  AnonymousInfo *AllocateAnonInfo(const domainname *service, const mDNSu8 *data, int len, const ResourceRecord *rr)
+{
+    AnonymousInfo *ai;
+    ai = (AnonymousInfo *)mDNSPlatformMemAllocate(sizeof(AnonymousInfo));
+    if (!ai)
+    {
+        return mDNSNULL;
+    }
+    mDNSPlatformMemZero(ai, sizeof(AnonymousInfo));
+    if (rr)
+    {
+        if (!CopyNSEC3ResourceRecord(ai, rr))
+        {
+            mDNSPlatformMemFree(ai);
+            return mDNSNULL;
+        }
+        return ai;
+    }
+    ai->salt = mDNSRandom(0xFFFFFFFF);
+    ai->AnonData = mDNSPlatformMemAllocate(len);
+    if (!ai->AnonData)
+    {
+        mDNSPlatformMemFree(ai);
+        return mDNSNULL;
+    }
+    ai->AnonDataLen = len;
+    mDNSPlatformMemCopy(ai->AnonData, data, len);
+    ai->nsec3RR = ConstructNSEC3Record(service, data, len, ai->salt);
+    if (!ai->nsec3RR)
+    {
+        mDNSPlatformMemFree(ai);
+        return mDNSNULL;
+    }
+    return ai;
+}
+
+mDNSexport void FreeAnonInfo(AnonymousInfo *ai)
+{
+    if (ai->nsec3RR)
+        mDNSPlatformMemFree(ai->nsec3RR);
+    if (ai->AnonData)
+        mDNSPlatformMemFree(ai->AnonData);
+    mDNSPlatformMemFree(ai);
+}
+
+mDNSexport void ReInitAnonInfo(AnonymousInfo **AnonInfo, const domainname *name)
+{
+    if (*AnonInfo)
+    {
+        AnonymousInfo *ai = *AnonInfo;
+        *AnonInfo = AllocateAnonInfo(name, ai->AnonData, ai->AnonDataLen, mDNSNULL);
+        if (!(*AnonInfo))
+            *AnonInfo = ai;
+        else
+            FreeAnonInfo(ai);
+    }
+}
+
+// This function should be used only if you know that the question and
+// the resource record belongs to the same set. The main usage is
+// in ProcessQuery where we find the question to be part of the same
+// set as the resource record, but it needs the AnonData to be
+// initialized so that it can walk the cache records to see if they
+// answer the question.
+mDNSexport void SetAnonData(DNSQuestion *q, ResourceRecord *rr, mDNSBool ForQuestion)
+{
+    if (!q->AnonInfo || !rr->AnonInfo)
+    {
+        LogMsg("SetAnonData: question %##s(%p), rr %##s(%p), NULL", q->qname.c, q->AnonInfo, rr->name->c, rr->AnonInfo);
+        return;
+    }
+    
+    debugf("SetAnonData: question %##s(%p), rr %##s(%p)", q->qname.c, q->AnonInfo, rr->name->c, rr->AnonInfo);
+    if (ForQuestion)
+    {
+        if (!q->AnonInfo->AnonData)
+        {
+            q->AnonInfo->AnonData = mDNSPlatformMemAllocate(rr->AnonInfo->AnonDataLen);
+            if (!q->AnonInfo->AnonData)
+                return;
+        }
+        mDNSPlatformMemCopy(q->AnonInfo->AnonData, rr->AnonInfo->AnonData, rr->AnonInfo->AnonDataLen);
+        q->AnonInfo->AnonDataLen = rr->AnonInfo->AnonDataLen;
+    }
+    else
+    {
+        if (!rr->AnonInfo->AnonData)
+        {
+            rr->AnonInfo->AnonData = mDNSPlatformMemAllocate(q->AnonInfo->AnonDataLen);
+            if (!rr->AnonInfo->AnonData)
+                return;
+        }
+        mDNSPlatformMemCopy(rr->AnonInfo->AnonData, q->AnonInfo->AnonData, q->AnonInfo->AnonDataLen);
+        rr->AnonInfo->AnonDataLen = q->AnonInfo->AnonDataLen;
+    }
+}
+
+// returns -1 if the caller should ignore the result
+// returns 1 if the record answers the question
+// returns 0 if the record does not answer the question
+mDNSexport int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
+{
+    mDNSexport mDNS mDNSStorage;
+    ResourceRecord *nsec3RR;
+    int i;
+    AnonymousInfo *qai, *rai;
+    mDNSu8 *AnonData;
+    int AnonDataLen;
+    rdataNSEC3 *nsec3;
+    int hlen;
+    const mDNSu8 hashName[NSEC3_MAX_HASH_LEN];
+    int nxtLength;
+    mDNSu8 *nxtName;
+
+    debugf("AnonInfoAnswersQuestion: question qname %##s", q->qname.c);
+
+    // Currently only PTR records can have anonymous information
+    if (q->qtype != kDNSType_PTR)
+    {
+        return -1;
+    }
+
+    // We allow anonymous questions to be answered by both normal services (without the
+    // anonymous information) and anonymous services that are part of the same set. And
+    // normal questions discover normal services and all anonymous services. 
+    //
+    // The three cases have been enumerated clearly even though they all behave the
+    // same way.
+    if (!q->AnonInfo)
+    {
+        debugf("AnonInfoAnswersQuestion: not a anonymous type question");
+        if (!rr->AnonInfo)
+        {
+            // case 1
+            return -1;
+        }
+        else
+        {
+            // case 2
+            debugf("AnonInfoAnswersQuestion: Question %##s not answered using anonymous record %##s", q->qname.c, rr->name->c);
+            return -1;
+        }
+    }
+    else
+    {
+        // case 3
+        if (!rr->AnonInfo)
+        {
+            debugf("AnonInfoAnswersQuestion: not a anonymous type record");
+            return -1;
+        }
+    }
+
+    // case 4: We have the anonymous information both in the question and the record. We need
+    // two sets of information to validate.
+    //
+    // 1) Anonymous data that identifies the set/group
+    // 2) NSEC3 record that contains the hash and the salt
+    //
+    // If the question is a remote one, it does not have the anonymous information to validate (just
+    // the NSEC3 record) and hence the anonymous data should come from the local resource record. If the
+    // question is local, it can come from either of them and if there is a mismatch between the
+    // question and record, it won't validate.
+
+    qai = q->AnonInfo;
+    rai = rr->AnonInfo;
+
+    if (qai->AnonData && rai->AnonData)
+    {
+        // Before a cache record is created, if there is a matching question i.e., part
+        // of the same set, then when the cache is created we also set the anonymous
+        // information. Otherwise, the cache record contains just the NSEC3 record and we
+        // won't be here for that case.
+        //
+        // It is also possible that a local question is matched against the local AuthRecord
+        // as that is also the case for which the AnonData would be non-NULL for both.
+        // We match questions against AuthRecords (rather than the cache) for LocalOnly case and 
+        // to see whether a .local query should be suppressed or not. The latter never happens
+        // because PTR queries are never suppressed.
+
+        // If they don't belong to the same anonymous set, then no point in validating.
+        if ((qai->AnonDataLen != rai->AnonDataLen) ||
+            mDNSPlatformMemCmp(qai->AnonData, rai->AnonData, qai->AnonDataLen) != 0)
+        {
+            debugf("AnonInfoAnswersQuestion: AnonData mis-match for record  %s question %##s ",
+                RRDisplayString(&mDNSStorage, rr), q->qname.c);
+            return 0;
+        }
+        // AnonData matches i.e they belong to the same group and the same service.
+        LogInfo("AnonInfoAnswersQuestion: Answering qname %##s, rname %##s, without validation", q->qname.c,
+            rr->name->c);
+        return 1;
+    }
+    else
+    {
+        debugf("AnonInfoAnswersQuestion: question %p, record %p", qai->AnonData, rai->AnonData);
+    }
+
+    if (qai->AnonData)
+    {
+        // If there is AnonData, then this is a local question. The
+        // NSEC3 RR comes from the resource record which could be part
+        // of the cache or local auth record. The cache entry could
+        // be from a remote host or created when we heard our own 
+        // announcements. In any case, we use that to see if it matches
+        // the question.
+        AnonData = qai->AnonData;
+        AnonDataLen = qai->AnonDataLen;
+        nsec3RR = rai->nsec3RR;
+    }
+    else
+    {
+        // Remote question or hearing our own question back
+        AnonData = rai->AnonData;
+        AnonDataLen = rai->AnonDataLen;
+        nsec3RR = qai->nsec3RR;
+    }
+
+    if (!AnonData || !nsec3RR)
+    {
+        // AnonData can be NULL for the cache entry and if we are hearing our own question back, AnonData is NULL for
+        // that too and we can end up here for that case.
+        debugf("AnonInfoAnswersQuestion: AnonData %p or nsec3RR %p, NULL for question %##s, record %s", AnonData, nsec3RR,
+            q->qname.c, RRDisplayString(&mDNSStorage, rr));
+        return 0;
+    }
+    debugf("AnonInfoAnswersQuestion: Validating question %##s, ResourceRecord %s", q->qname.c, RRDisplayString(&mDNSStorage, nsec3RR));
+
+
+    nsec3 = (rdataNSEC3 *)nsec3RR->rdata->u.data;
+
+    if (!NSEC3HashName(nsec3RR->name, nsec3, AnonData, AnonDataLen, hashName, &hlen))
+    {
+        LogMsg("AnonInfoAnswersQuestion: NSEC3HashName failed for ##s", nsec3RR->name->c);
+        return mDNSfalse;
+    }
+    if (hlen != SHA1_HASH_LENGTH)
+    {
+        LogMsg("AnonInfoAnswersQuestion: hlen wrong %d", hlen);
+        return mDNSfalse;
+    }
+
+    NSEC3Parse(nsec3RR, mDNSNULL, &nxtLength, &nxtName, mDNSNULL, mDNSNULL);
+
+    if (hlen != nxtLength)
+    {
+        LogMsg("AnonInfoAnswersQuestion: ERROR!! hlen %d not same as nxtLength %d", hlen, nxtLength);
+        return mDNSfalse;
+    }
+
+    for (i = 0; i < nxtLength; i++)
+    {
+        if (nxtName[i] != hashName[i])
+        {
+            debugf("AnonInfoAnswersQuestion: mismatch output %x, digest %x, i %d", nxtName[i+1], hashName[i], i);
+            return 0;
+        }
+    }
+    LogInfo("AnonInfoAnswersQuestion: ResourceRecord %s matched question %##s (%s)", RRDisplayString(&mDNSStorage, nsec3RR), q->qname.c, DNSTypeName(q->qtype));
+    return 1;
+}
+
+// Find a matching NSEC3 record for the name. We parse the questions and the records in the packet in order.
+// Similarly we also parse the NSEC3 records in order and this mapping to the questions and records
+// respectively.
+mDNSlocal CacheRecord *FindMatchingNSEC3ForName(mDNS *const m, CacheRecord **nsec3, const domainname *name)
+{
+    CacheRecord *cr;
+    CacheRecord **prev = nsec3;
+    
+    (void) m;
+
+    for (cr = *nsec3; cr; cr = cr->next)
+    {
+        if (SameDomainName(cr->resrec.name, name))
+        {
+            debugf("FindMatchingNSEC3ForName: NSEC3 record %s matched %##s", CRDisplayString(m, cr), name->c);
+            *prev = cr->next;
+            cr->next = mDNSNULL;
+            return cr;
+        }
+        prev = &cr->next;
+    }
+    return mDNSNULL;
+}
+
+mDNSexport void InitializeAnonInfoForQuestion(mDNS *const m, CacheRecord **McastNSEC3Records, DNSQuestion *q)
+{
+    CacheRecord *nsec3CR;
+
+    if (q->qtype != kDNSType_PTR)
+        return;
+
+    nsec3CR = FindMatchingNSEC3ForName(m, McastNSEC3Records, &q->qname);
+    if (nsec3CR)
+    {
+        q->AnonInfo = AllocateAnonInfo(mDNSNULL, mDNSNULL, 0, &nsec3CR->resrec);
+        if (q->AnonInfo)
+        {
+            debugf("InitializeAnonInfoForQuestion: Found a matching NSEC3 record %s, for %##s (%s)",
+                RRDisplayString(m, q->AnonInfo->nsec3RR), q->qname.c, DNSTypeName(q->qtype));
+        }
+        ReleaseCacheRecord(m, nsec3CR);
+    }
+}
+
+mDNSexport void InitializeAnonInfoForCR(mDNS *const m, CacheRecord **McastNSEC3Records, CacheRecord *cr)
+{
+    CacheRecord *nsec3CR;
+
+    if (!(*McastNSEC3Records))
+        return;
+
+    // If already initialized or not a PTR type, we don't have to do anything
+    if (cr->resrec.AnonInfo || cr->resrec.rrtype != kDNSType_PTR)
+        return;
+
+    nsec3CR = FindMatchingNSEC3ForName(m, McastNSEC3Records, cr->resrec.name);
+    if (nsec3CR)
+    {
+        cr->resrec.AnonInfo = AllocateAnonInfo(mDNSNULL, mDNSNULL, 0, &nsec3CR->resrec);
+        if (cr->resrec.AnonInfo)
+        {
+            debugf("InitializeAnonInfoForCR: Found a matching NSEC3 record %s, for %##s (%s)",
+                RRDisplayString(m, cr->resrec.AnonInfo->nsec3RR), cr->resrec.name->c,
+                DNSTypeName(cr->resrec.rrtype));
+        }
+        ReleaseCacheRecord(m, nsec3CR);
+    }
+}
+
+mDNSexport mDNSBool IdenticalAnonInfo(AnonymousInfo *a1, AnonymousInfo *a2)
+{
+    // if a1 is NULL and a2 is not NULL AND vice-versa
+    // return false as there is a change.
+    if ((a1 != mDNSNULL) != (a2 != mDNSNULL))
+        return mDNSfalse;
+
+    // Both could be NULL or non-NULL
+    if (a1 && a2)
+    {
+        // The caller already verified that the owner name is the same.
+        // Check whether the RData is same.
+        if (!IdenticalSameNameRecord(a1->nsec3RR, a2->nsec3RR))
+        {
+            debugf("IdenticalAnonInfo: nsec3RR mismatch");
+            return mDNSfalse;
+        }
+    }
+    return mDNStrue;
+}
+
+mDNSexport void CopyAnonInfoForCR(mDNS *const m, CacheRecord *crto, CacheRecord *crfrom)
+{
+    AnonymousInfo *aifrom = crfrom->resrec.AnonInfo;
+    AnonymousInfo *aito = crto->resrec.AnonInfo;
+
+    (void) m;
+
+    if (!aifrom)
+        return;
+
+    if (aito)
+    {
+        crto->resrec.AnonInfo = aifrom;
+        FreeAnonInfo(aito);
+        crfrom->resrec.AnonInfo = mDNSNULL;
+    }
+    else
+    {
+        FreeAnonInfo(aifrom);
+        crfrom->resrec.AnonInfo = mDNSNULL;
+    }
+}
+
+#else // !ANONYMOUS_DISABLED
+
+mDNSexport void ReInitAnonInfo(AnonymousInfo **si, const domainname *name)
+{
+       (void)si;
+       (void)name;
+}
+
+mDNSexport AnonymousInfo * AllocateAnonInfo(const domainname *service, const mDNSu8 *AnonData, int len, const ResourceRecord *rr)
+{
+       (void)service;
+       (void)AnonData;
+       (void)len;
+       (void)rr;
+
+       return mDNSNULL;
+}
+
+mDNSexport void FreeAnonInfo(AnonymousInfo *ai)
+{
+       (void)ai;
+}
+
+mDNSexport void SetAnonData(DNSQuestion *q, ResourceRecord *rr, mDNSBool ForQuestion)
+{
+       (void)q;
+       (void)rr;
+       (void)ForQuestion;
+}
+
+mDNSexport int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
+{
+       (void)rr;
+       (void)q;
+
+       return mDNSfalse;
+}
+
+mDNSexport void InitializeAnonInfoForQuestion(mDNS *const m, CacheRecord **McastNSEC3Records, DNSQuestion *q)
+{
+       (void)m;
+       (void)McastNSEC3Records;
+       (void)q;
+}
+
+mDNSexport void InitializeAnonInfoForCR(mDNS *const m, CacheRecord **McastNSEC3Records, CacheRecord *cr)
+{
+       (void)m;
+       (void)McastNSEC3Records;
+       (void)cr;
+}
+
+mDNSexport void CopyAnonInfoForCR(mDNS *const m, CacheRecord *crto, CacheRecord *crfrom)
+{
+       (void)m;
+       (void)crto;
+       (void)crfrom;
+}
+
+mDNSexport mDNSBool IdenticalAnonInfo(AnonymousInfo *a1, AnonymousInfo *a2)
+{
+       (void)a1;
+       (void)a2;
+
+       return mDNStrue;
+}
+
+#endif // !ANONYMOUS_DISABLED
diff --git a/mDNSCore/anonymous.h b/mDNSCore/anonymous.h
new file mode 100644 (file)
index 0000000..2f2b4f8
--- /dev/null
@@ -0,0 +1,31 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2012 Apple Computer, 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 __ANONYMOUS_H_
+#define __ANONYMOUS_H_
+
+extern void ReInitAnonInfo(AnonymousInfo **si, const domainname *name);
+extern AnonymousInfo *AllocateAnonInfo(const domainname *service, const mDNSu8 *AnonData, int len, const ResourceRecord *rr);
+extern void FreeAnonInfo(AnonymousInfo *ai);
+extern void SetAnonData(DNSQuestion *q, ResourceRecord *rr, mDNSBool ForQuestion);
+extern int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q);
+extern void InitializeAnonInfoForCR(mDNS *const m, CacheRecord **McastNSEC3Records, CacheRecord *cr);
+extern void InitializeAnonInfoForQuestion(mDNS *const m, CacheRecord **McastNSEC3Records, DNSQuestion *q);
+extern void CopyAnonInfoForCR(mDNS *const m, CacheRecord *crto, CacheRecord *crfrom);
+extern mDNSBool IdenticalAnonInfo(AnonymousInfo *a1, AnonymousInfo *a2);
+
+#endif
diff --git a/mDNSCore/dnsproxy.c b/mDNSCore/dnsproxy.c
new file mode 100644 (file)
index 0000000..11bacc1
--- /dev/null
@@ -0,0 +1,836 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2011 Apple Computer, 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 "dnsproxy.h"
+
+#ifndef UNICAST_DISABLED
+
+// Implementation Notes
+//
+// DNS Proxy listens on port 53 (UDPv4v6 & TCPv4v6) for DNS queries. It handles only
+// the "Query" opcode of the DNS protocol described in RFC 1035. For all other opcodes, it returns
+// "Not Implemented" error. The platform interface mDNSPlatformInitDNSProxySkts
+// sets up the sockets and whenever it receives a packet, it calls ProxyTCPCallback or ProxyUDPCallback
+// defined here. For TCP socket, the platform does the "accept" and only sends the received packets
+// on the newly accepted socket. A single UDP socket (per address family) is used to send/recv
+// requests/responses from all clients. For TCP, there is one socket per request. Hence, there is some
+// extra state that needs to be disposed at the end.
+//
+// When a DNS request is received, ProxyCallbackCommon checks for malformed packet etc. and also checks
+// for duplicates, before creating DNSProxyClient state and starting a question with the "core"
+// (mDNS_StartQuery). When the callback for the question happens, it gathers all the necessary
+// resource records, constructs a response and sends it back to the client.
+//
+//   - Question callback is called with only one resource record at a time. We need all the resource
+//     records to construct the response. Hence, we lookup all the records ourselves. 
+//
+//   - The response may not fit the client's buffer size. In that case, we need to set the truncate bit
+//     and the client would retry using TCP.
+//
+//   - The client may have set the DNSSEC OK bit in the EDNS0 option and that means we also have to
+//     return the RRSIGs or the NSEC records with the RRSIGs in the Additional section. We need to
+//     ask the "core" to fetch the DNSSEC records and do the validation if the CD bit is not set.
+//
+// Once the response is sent to the client, the client state is disposed. When there is no response
+// from the "core", it eventually times out and we will not find any answers in the cache and we send a
+// "NXDomain" response back. Thus, we don't need any special timers to reap the client state in the case
+// of errors. 
+
+typedef struct DNSProxyClient_struct DNSProxyClient;
+
+struct DNSProxyClient_struct {
+
+    DNSProxyClient *next; 
+    mDNSAddr    addr;               // Client's IP address 
+    mDNSIPPort  port;               // Client's port number
+    mDNSOpaque16 msgid;             // DNS msg id
+    mDNSInterfaceID interfaceID;    // Interface on which we received the request
+    void *socket;                   // Return socket
+    mDNSBool tcp;                   // TCP or UDP ?
+    mDNSOpaque16 requestFlags;      // second 16 bit word in the DNSMessageHeader of the request
+    mDNSu8 *optRR;                  // EDNS0 option
+    mDNSu16 optLen;                 // Total Length of the EDNS0 option 
+    mDNSu16 rcvBufSize;             // How much can the client receive ?
+    mDNSBool DNSSECOK;              // DNSSEC OK ?
+    void *context;                  // Platform context to be disposed if non-NULL
+    domainname qname;               // q->qname can't be used for duplicate check
+    DNSQuestion q;                  // as it can change underneath us for CNAMEs
+};
+
+#define MIN_DNS_MESSAGE_SIZE    512
+DNSProxyClient *DNSProxyClients;
+
+mDNSlocal void FreeDNSProxyClient(DNSProxyClient *pc)
+{
+    if (pc->optRR)
+        mDNSPlatformMemFree(pc->optRR);
+    mDNSPlatformMemFree(pc);
+}
+
+mDNSlocal mDNSBool ParseEDNS0(DNSProxyClient *pc, const mDNSu8 *ptr, int length, const mDNSu8 *limit)
+{
+    mDNSu16 rrtype, rrclass;
+    mDNSu8 rcode, version;
+    mDNSu16 flag;
+    if (ptr + length > limit)
+    {
+        LogInfo("ParseEDNS0: Not enough space in the packet");
+        return mDNSfalse;
+    }
+    // Skip the root label
+    ptr++;
+    rrtype  = (mDNSu16) ((mDNSu16)ptr[0] <<  8 | ptr[1]);
+    if (rrtype != kDNSType_OPT)
+    {
+        LogInfo("ParseEDNS0: Not the right type %d", rrtype);
+        return mDNSfalse;
+    }
+    rrclass = (mDNSu16) ((mDNSu16)ptr[2] <<  8 | ptr[3]);
+    rcode   = ptr[4];
+    version = ptr[5];
+    flag    = (mDNSu16) ((mDNSu16)ptr[6] << 8 | ptr[7]);
+
+    debugf("rrtype is %s, length is %d, rcode %d, version %d, flag 0x%x", DNSTypeName(rrtype), rrclass, rcode, version, flag);
+    pc->rcvBufSize = rrclass;
+    pc->DNSSECOK = ptr[6] & 0x80;
+    
+    return mDNStrue;
+}
+
+mDNSexport mDNSu8 *DNSProxySetAttributes(DNSQuestion *q, DNSMessageHeader *h, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *limit)
+{
+    DNSProxyClient *pc = (DNSProxyClient *)q->QuestionContext;
+
+    (void) msg;
+
+    h->flags = pc->requestFlags;
+    if (pc->optRR)
+    {
+        if (ptr + pc->optLen > limit)
+        {
+            LogInfo("DNSProxySetAttributes: Cannot set EDNS0 option start %p, OptLen %d, end %p", ptr, pc->optLen, limit);
+            return ptr;
+        }
+        h->numAdditionals++;
+        mDNSPlatformMemCopy(ptr, pc->optRR, pc->optLen);
+        ptr += pc->optLen;
+    }
+    return ptr;
+}
+
+mDNSlocal mDNSu8 *AddEDNS0Option(mDNS *const m, mDNSu8 *ptr, mDNSu8 *limit)
+{
+    int len = 4096;
+
+    if (ptr + 11 > limit)
+    {
+        LogInfo("AddEDNS0Option: not enough space");
+        return mDNSNULL;
+    }
+    m->omsg.h.numAdditionals++;
+    ptr[0] = 0;
+    ptr[1] = (mDNSu8) (kDNSType_OPT >> 8);
+    ptr[2] = (mDNSu8) (kDNSType_OPT & 0xFF);
+    ptr[3] = (mDNSu8) (len >> 8);
+    ptr[4] = (mDNSu8) (len & 0xFF);
+    ptr[5] = 0;     // rcode
+    ptr[6] = 0;     // version
+    ptr[7] = 0;
+    ptr[8] = 0;     // flags
+    ptr[9] = 0;     // rdlength
+    ptr[10] = 0;    // rdlength
+
+    debugf("AddEDNS0 option");
+
+    return (ptr + 11);
+}
+
+// Currently RD and CD bit should be copied if present in the request or cleared if
+// not present in the request. RD bit is normally set in the response and hence the
+// cache reflects the right value. CD bit behaves differently. If the CD bit is set
+// the first time, the cache retains it, if it is present in response (assuming the
+// upstream server does it right). Next time through we should not use the cached
+// value of the CD bit blindly. It depends on whether it was in the request or not.
+mDNSlocal mDNSOpaque16 SetResponseFlags(DNSProxyClient *pc, const mDNSOpaque16 responseFlags)
+{
+    mDNSOpaque16 rFlags = responseFlags;
+
+    if (pc->requestFlags.b[0] & kDNSFlag0_RD)
+        rFlags.b[0] |= kDNSFlag0_RD;
+    else
+        rFlags.b[0] &= ~kDNSFlag0_RD;
+
+    if (pc->requestFlags.b[1] & kDNSFlag1_CD)
+        rFlags.b[1] |= kDNSFlag1_CD;
+    else
+        rFlags.b[1] &= ~kDNSFlag1_CD;
+
+    return rFlags;
+}
+
+mDNSlocal mDNSu8 *AddResourceRecords(mDNS *const m, DNSProxyClient *pc, mDNSu8 **prevptr, mStatus *error)
+{
+    mDNSu32 slot;
+    CacheGroup *cg;
+    CacheRecord *cr;
+    int len = sizeof(DNSMessageHeader);
+    mDNSu8 *orig = m->omsg.data;
+    mDNSBool first = mDNStrue;
+    mDNSu8 *ptr = mDNSNULL;
+    mDNSs32 now;
+    mDNSs32 ttl;
+    CacheRecord *nsec = mDNSNULL;
+    CacheRecord *soa = mDNSNULL;
+    CacheRecord *cname = mDNSNULL;
+    mDNSu8 *limit;
+
+    *error = mStatus_NoError;
+    *prevptr = mDNSNULL;
+
+    mDNS_Lock(m);
+    now = m->timenow;
+    mDNS_Unlock(m);
+
+    if (!pc->tcp)
+    {
+        if (!pc->rcvBufSize)
+        {
+            limit = m->omsg.data + MIN_DNS_MESSAGE_SIZE;
+        }
+        else
+        {
+            limit = (pc->rcvBufSize > AbsoluteMaxDNSMessageData ? m->omsg.data + AbsoluteMaxDNSMessageData : m->omsg.data + pc->rcvBufSize);
+        }
+    }
+    else
+    {
+        // For TCP, limit is not determined by EDNS0 but by 16 bit rdlength field and
+        // AbsoluteMaxDNSMessageData is smaller than 64k.
+        limit = m->omsg.data + AbsoluteMaxDNSMessageData;
+    }
+    LogInfo("AddResourceRecords: Limit is %d", limit - m->omsg.data);
+
+    if (!SameDomainName(&pc->qname, &pc->q.qname))
+    {
+        AssignDomainName(&pc->q.qname, &pc->qname);
+        pc->q.qnamehash = DomainNameHashValue(&pc->q.qname);
+    }
+
+again:
+    nsec = soa = cname = mDNSNULL;
+    slot = HashSlot(&pc->q.qname);
+        
+    cg = CacheGroupForName(m, slot, pc->q.qnamehash, &pc->q.qname);
+    if (!cg)
+    {
+        LogInfo("AddResourceRecords: CacheGroup not found");
+        *error = mStatus_NoSuchRecord;
+        return mDNSNULL;
+    }
+    // Set ValidatingResponse so that you can get RRSIGs also matching
+    // the question
+    if (pc->DNSSECOK)
+        pc->q.ValidatingResponse = 1;
+    for (cr = cg->members; cr; cr = cr->next)
+    {
+        if (SameNameRecordAnswersQuestion(&cr->resrec, &pc->q))
+        {
+            if (first)
+            {
+                // If this is the first time, initialize the header and the question.
+                // This code needs to be here so that we can use the responseFlags from the
+                // cache record
+                mDNSOpaque16 responseFlags = SetResponseFlags(pc, cr->responseFlags);
+                InitializeDNSMessage(&m->omsg.h, pc->msgid, responseFlags);
+                ptr = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, &pc->qname, pc->q.qtype, pc->q.qclass);
+                if (!ptr)
+                {
+                    LogInfo("AddResourceRecords: putQuestion NULL for %##s (%s)", &pc->qname.c, DNSTypeName(pc->q.qtype));
+                    return mDNSNULL;
+                }
+                first = mDNSfalse;
+            }
+            // - For NegativeAnswers there is nothing to add
+            // - If DNSSECOK is set, we also automatically lookup the RRSIGs which
+            //   will also be returned. If the client is explicitly looking up
+            //   a DNSSEC record (e.g., DNSKEY, DS) we should return the response.
+            //   DNSSECOK bit only influences whether we add the RRSIG or not.
+            if (cr->resrec.RecordType != kDNSRecordTypePacketNegative)
+            {
+                LogInfo("AddResourceRecords: Answering question with %s", CRDisplayString(m, cr));
+                ttl = cr->resrec.rroriginalttl - (now - cr->TimeRcvd) / mDNSPlatformOneSecond;
+                ptr = PutResourceRecordTTLWithLimit(&m->omsg, ptr, &m->omsg.h.numAnswers, &cr->resrec, ttl, limit);
+                if (!ptr)
+                {
+                    *prevptr = orig;
+                    return mDNSNULL;
+                }
+                len += (ptr - orig); 
+                orig = ptr;
+            }
+            // If we have nsecs (wildcard expanded answer or negative response), add them
+            // in the additional section below if the DNSSECOK bit is set
+            if (pc->DNSSECOK && cr->nsec)
+            {
+                LogInfo("AddResourceRecords: nsec set for %s", CRDisplayString(m ,cr));
+                nsec = cr->nsec;
+            }
+            if (cr->soa)
+            {
+                LogInfo("AddResourceRecords: soa set for %s", CRDisplayString(m ,cr));
+                soa = cr->soa;
+            }
+            // If we are using CNAME to answer a question and CNAME is not the type we
+            // are looking for, note down the CNAME record so that we can follow them
+            // later. Before we follow the CNAME, print the RRSIGs and any nsec (wildcard
+            // expanded) if any.
+            if ((pc->q.qtype != cr->resrec.rrtype) && cr->resrec.rrtype == kDNSType_CNAME)
+            {
+                LogInfo("AddResourceRecords: cname set for %s", CRDisplayString(m ,cr));
+                cname = cr;
+            }
+        }
+    }
+    // Along with the nsec records, we also cache the SOA record. For non-DNSSEC question, we need
+    // to send the SOA back. Normally we either cache the SOA record (non-DNSSEC question) pointed
+    // to by "cr->soa" or the NSEC/SOA records along with their RRSIGs (DNSSEC question) pointed to
+    // by "cr->nsec". Two cases:
+    //
+    // - if we issue a DNSSEC question followed by non-DNSSEC question for the same name,
+    //   we only have the nsec records and we need to filter the SOA record alone for the
+    //   non-DNSSEC questions.
+    //
+    // - if we issue a non-DNSSEC question followed by DNSSEC question for the same name,
+    //   the "core" flushes the cache entry and re-issue the question with EDNS0/DOK bit and
+    //   in this case we return all the DNSSEC records we have.
+    for (; nsec; nsec = nsec->next)
+    {
+        if (!pc->DNSSECOK && DNSSECRecordType(nsec->resrec.rrtype))
+            continue;
+        LogInfo("AddResourceRecords:NSEC Answering question with %s", CRDisplayString(m, nsec));
+        ttl = nsec->resrec.rroriginalttl - (now - nsec->TimeRcvd) / mDNSPlatformOneSecond;
+        ptr = PutResourceRecordTTLWithLimit(&m->omsg, ptr, &m->omsg.h.numAuthorities, &nsec->resrec, ttl, limit);
+        if (!ptr)
+        {
+            *prevptr = orig;
+            return mDNSNULL;
+        }
+        len += (ptr - orig); 
+        orig = ptr;
+    }
+    if (soa)
+    {
+        LogInfo("AddResourceRecords: SOA Answering question with %s", CRDisplayString(m, soa));
+        ptr = PutResourceRecordTTLWithLimit(&m->omsg, ptr, &m->omsg.h.numAuthorities, &soa->resrec, soa->resrec.rroriginalttl, limit);
+        if (!ptr)
+        {
+            *prevptr = orig;
+            return mDNSNULL;
+        }
+        len += (ptr - orig); 
+        orig = ptr;
+    }
+    if (cname)
+    {
+        AssignDomainName(&pc->q.qname, &cname->resrec.rdata->u.name);
+        pc->q.qnamehash = DomainNameHashValue(&pc->q.qname);
+        goto again;
+    }
+    if (!ptr)
+    {
+        LogInfo("AddResourceRecords: Did not find any valid ResourceRecords");
+        *error = mStatus_NoSuchRecord;
+        return mDNSNULL;
+    }
+    if (pc->rcvBufSize)
+    {
+        ptr = AddEDNS0Option(m, ptr, limit);
+        if (!ptr)
+        {
+            *prevptr = orig;
+            return mDNSNULL;
+        }
+        len += (ptr - orig); 
+        orig = ptr;
+    }
+    LogInfo("AddResourceRecord: Added %d bytes to the packet", len);
+    return ptr;
+}
+
+mDNSlocal void ProxyClientCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+{
+    DNSProxyClient *pc = question->QuestionContext;
+    DNSProxyClient **ppc = &DNSProxyClients;
+    mDNSu8 *ptr;
+    mDNSu8 *prevptr;
+    mStatus error;
+
+    if (!AddRecord)
+        return;
+
+    LogInfo("ProxyClientCallback: ResourceRecord %s", RRDisplayString(m, answer));
+
+    // We asked for validation and not timed out yet, then wait for the DNSSEC result.
+    // We have to set the AD bit in the response if it is secure which can't be done
+    // till we get the DNSSEC result back (indicated by QC_dnssec).
+    if (question->ValidationRequired)
+    {
+        mDNSs32 now;
+
+        mDNS_Lock(m);
+        now = m->timenow;
+        mDNS_Unlock(m);
+        if (((now - question->StopTime) < 0) && AddRecord != QC_dnssec)
+        {
+            LogInfo("ProxyClientCallback: No DNSSEC answer yet for Question %##s (%s), AddRecord %d, answer %s", question->qname.c,
+                DNSTypeName(question->qtype), AddRecord, RRDisplayString(m, answer));
+            return;
+        }
+    }
+
+    if (answer->RecordType != kDNSRecordTypePacketNegative)
+    {
+        if (answer->rrtype != question->qtype)
+        {
+            // Wait till we get called for the real response
+            LogInfo("ProxyClientCallback: Received %s, not answering yet", RRDisplayString(m, answer));
+            return;
+        }
+    }
+    ptr = AddResourceRecords(m, pc, &prevptr, &error);
+    if (!ptr)
+    {
+        LogInfo("ProxyClientCallback: AddResourceRecords NULL for %##s (%s)", &pc->qname.c, DNSTypeName(pc->q.qtype));
+        if (error == mStatus_NoError && prevptr)
+        {
+            // No space to add the record. Set the Truncate bit for UDP.
+            //
+            // TBD: For TCP, we need to send the rest of the data. But finding out what is left
+            // is harder. We should allocate enough buffer in the first place to send all
+            // of the data.
+            if (!pc->tcp)
+            {
+                m->omsg.h.flags.b[0] |= kDNSFlag0_TC;
+                ptr = prevptr;
+            }
+            else
+            {
+                LogInfo("ProxyClientCallback: ERROR!! Not enough space to return in TCP for %##s (%s)", &pc->qname.c, DNSTypeName(pc->q.qtype));
+                ptr = prevptr;
+            }
+        }
+        else
+        {
+            mDNSOpaque16 flags   = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery, kDNSFlag1_RC_ServFail } };
+            // We could not find the record for some reason. Return a response, so that the client
+            // is not waiting forever.
+            LogInfo("ProxyClientCallback: No response");
+            if (!mDNSOpaque16IsZero(pc->q.responseFlags))
+                flags = pc->q.responseFlags;
+            InitializeDNSMessage(&m->omsg.h, pc->msgid, flags);
+            ptr = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, &pc->qname, pc->q.qtype, pc->q.qclass);
+            if (!ptr)
+            {
+                LogInfo("ProxyClientCallback: putQuestion NULL for %##s (%s)", &pc->qname.c, DNSTypeName(pc->q.qtype));
+                goto done;
+            }
+        }
+    }
+    if (question->ValidationRequired)
+    {
+        if (question->ValidationState == DNSSECValDone && question->ValidationStatus == DNSSEC_Secure)
+        {
+            LogInfo("ProxyClientCallback: Setting AD bit for Question %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
+            m->omsg.h.flags.b[1] |= kDNSFlag1_AD;
+        }
+        else
+        {
+            // If some external resolver sets the AD bit and we did not validate the response securely, don't set
+            // the AD bit. It is possible that we did not see all the records that the upstream resolver saw or
+            // a buggy implementation somewhere.
+            if (m->omsg.h.flags.b[1] & kDNSFlag1_AD)
+            {
+                LogInfo("ProxyClientCallback: AD bit set in the response for response that was not validated locally %##s (%s)",
+                    question->qname.c, DNSTypeName(question->qtype));
+                m->omsg.h.flags.b[1] &= ~kDNSFlag1_AD;
+            }
+        }
+    }
+
+    if (!pc->tcp)
+    {
+        mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, (UDPSocket *)pc->socket, &pc->addr, pc->port, mDNSNULL, mDNSNULL, mDNSfalse);
+    }
+    else
+    {
+        mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, mDNSNULL, &pc->addr, pc->port, (TCPSocket *)pc->socket, mDNSNULL, mDNSfalse);
+    }
+
+done:
+    mDNS_StopQuery(m, question);
+
+    while (*ppc && *ppc != pc)
+        ppc=&(*ppc)->next;
+    if (!*ppc)
+    {
+        LogMsg("ProxyClientCallback: question %##s (%s) not found", question->qname.c, DNSTypeName(question->qtype));
+        return;
+    }
+    *ppc = pc->next;
+    mDNSPlatformDisposeProxyContext(pc->context);
+    FreeDNSProxyClient(pc);
+}
+
+mDNSlocal void SendError(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *dstaddr,
+    const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, mDNSBool tcp, void *context, mDNSu8 rcode)
+{
+    int pktlen = (int)(end - (mDNSu8 *)pkt);
+    DNSMessage  *msg  = (DNSMessage *)pkt;
+
+    (void) InterfaceID; 
+
+    // RFC 1035 requires that we copy the question back and RFC 2136 is okay with sending nothing
+    // in the body or send back whatever we get for updates. It is easy to return whatever we get
+    // in the question back to the responder. We return as much as we can fit in our standard
+    // output packet.
+    if (pktlen > AbsoluteMaxDNSMessageData)
+        pktlen = AbsoluteMaxDNSMessageData;
+
+    mDNSPlatformMemCopy(&m->omsg.h, &msg->h, sizeof(DNSMessageHeader));
+    m->omsg.h.flags.b[0] |= kDNSFlag0_QR_Response;
+    m->omsg.h.flags.b[1] = rcode;
+    mDNSPlatformMemCopy(m->omsg.data, (mDNSu8 *)&msg->h.numQuestions, pktlen);
+    if (!tcp)
+    {
+        mDNSSendDNSMessage(m, &m->omsg, (mDNSu8 *)&m->omsg + pktlen, mDNSInterface_Any, socket, dstaddr, dstport, mDNSNULL, mDNSNULL,
+            mDNSfalse);
+    }
+    else
+    {
+        mDNSSendDNSMessage(m, &m->omsg, (mDNSu8 *)&m->omsg + pktlen, mDNSInterface_Any, mDNSNULL, dstaddr, dstport, (TCPSocket *)socket,
+            mDNSNULL, mDNSfalse);
+    }
+    mDNSPlatformDisposeProxyContext(context);
+}
+
+mDNSlocal DNSQuestion *IsDuplicateClient(const mDNS *const m, const mDNSAddr *const addr, const mDNSIPPort port, const mDNSOpaque16 id,
+    const DNSQuestion *const question)
+{
+    DNSProxyClient *pc;
+
+    (void) m; // unused
+
+    for (pc = DNSProxyClients; pc; pc = pc->next)
+    {
+        if (mDNSSameAddress(&pc->addr, addr)   &&
+            mDNSSameIPPort(pc->port, port)  &&
+            mDNSSameOpaque16(pc->msgid, id) &&
+            pc->q.qtype == question->qtype  &&
+            pc->q.qclass  == question->qclass &&
+            SameDomainName(&pc->qname, &question->qname))
+        {
+            LogInfo("IsDuplicateClient: Found a duplicate client in the list");
+            return(&pc->q);
+        }
+    }
+    return(mDNSNULL);
+}
+
+mDNSlocal mDNSBool CheckDNSProxyIpIntf(const mDNS *const m, mDNSInterfaceID InterfaceID)
+{
+    int i;
+    mDNSu32 ip_ifindex = (mDNSu32)(unsigned long)InterfaceID;
+
+    LogInfo("CheckDNSProxyIpIntf: Stored Input Interface List: [%d] [%d] [%d] [%d] [%d]", m->dp_ipintf[0], m->dp_ipintf[1], m->dp_ipintf[2], 
+             m->dp_ipintf[3], m->dp_ipintf[4]);
+
+    for (i = 0; i < MaxIp; i++)
+    {
+        if (ip_ifindex == m->dp_ipintf[i])
+            return mDNStrue;
+    }
+    return mDNSfalse;
+
+}
+
+mDNSlocal void ProxyCallbackCommon(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr,
+    const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, mDNSBool tcp, void *context)
+{
+    DNSMessage  *msg  = (DNSMessage *)pkt;
+    mDNSu8 QR_OP;
+    const mDNSu8 *ptr;
+    DNSQuestion q, *qptr;
+    DNSProxyClient *pc;
+    const mDNSu8 *optRR;
+    int optLen = 0;
+    DNSProxyClient **ppc = &DNSProxyClients;
+
+    (void) dstaddr;
+    (void) dstport;
+
+    debugf("ProxyCallbackCommon: DNS Query coming from InterfaceID %p", InterfaceID);
+    // Ignore if the DNS Query is not from a Valid Input InterfaceID
+    if (!CheckDNSProxyIpIntf(m, InterfaceID))
+        return;
+    
+    if ((unsigned)(end - (mDNSu8 *)pkt) < sizeof(DNSMessageHeader))
+    {
+        debugf("ProxyCallbackCommon: DNS Message from %#a:%d to %#a:%d length %d too short", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt);
+        return;
+    }
+
+    QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask);
+    if (QR_OP != kDNSFlag0_QR_Query)
+    {
+        LogInfo("ProxyCallbackCommon: Not a query(%d) for pkt from %#a:%d", QR_OP, srcaddr, mDNSVal16(srcport));
+        SendError(m, socket, pkt, end, srcaddr, srcport, InterfaceID, tcp, context, kDNSFlag1_RC_NotImpl);
+        return;
+    }
+
+    // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
+    ptr = (mDNSu8 *)&msg->h.numQuestions;
+    msg->h.numQuestions   = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
+    msg->h.numAnswers     = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
+    msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]);
+    msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]);
+
+    if (msg->h.numQuestions != 1 || msg->h.numAnswers || msg->h.numAuthorities)
+    {
+        LogInfo("ProxyCallbackCommon: Malformed pkt from %#a:%d, Q:%d, An:%d, Au:%d", srcaddr, mDNSVal16(srcport),
+            msg->h.numQuestions, msg->h.numAnswers, msg->h.numAuthorities);
+        SendError(m, socket, pkt, end, srcaddr, srcport, InterfaceID, tcp, context, kDNSFlag1_RC_FormErr);
+        return;
+    }
+    ptr = msg->data;
+    ptr = getQuestion(msg, ptr, end, InterfaceID, &q);
+    if (!ptr)
+    {
+        LogInfo("ProxyCallbackCommon: Question cannot be parsed for pkt from %#a:%d", srcaddr, mDNSVal16(srcport));
+        SendError(m, socket, pkt, end, srcaddr, srcport, InterfaceID, tcp, context, kDNSFlag1_RC_FormErr);
+        return;
+    }
+    else
+    {
+        LogInfo("ProxyCallbackCommon: Question %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
+    }
+    ptr = LocateOptRR(msg, end, 0);
+    if (ptr)
+    {
+        optRR = ptr;
+        ptr = skipResourceRecord(msg, ptr, end);
+        // Be liberal and ignore the EDNS0 option if we can't parse it properly
+        if (!ptr)
+        {
+            LogInfo("ProxyCallbackCommon: EDNS0 cannot be parsed for pkt from %#a:%d, ignoring", srcaddr, mDNSVal16(srcport));
+        }
+        else
+        {
+            optLen = ptr - optRR;
+            LogInfo("ProxyCallbackCommon: EDNS0 opt length %d present in Question %##s (%s)", optLen, q.qname.c, DNSTypeName(q.qtype));
+        }
+    }
+    else
+    {
+        LogInfo("ProxyCallbackCommon: EDNS0 opt not present in Question %##s (%s), ptr %p", q.qname.c, DNSTypeName(q.qtype), ptr);
+    }
+        
+    qptr = IsDuplicateClient(m, srcaddr, srcport, msg->h.id, &q);
+    if (qptr)
+    {
+        LogInfo("ProxyCallbackCommon: Found a duplicate for pkt from %#a:%d, ignoring this", srcaddr, mDNSVal16(srcport));
+        return;
+    }
+    pc = mDNSPlatformMemAllocate(sizeof(DNSProxyClient));
+    if (!pc)
+    {
+        LogMsg("ProxyCallbackCommon: Memory failure for pkt from %#a:%d, ignoring this", srcaddr, mDNSVal16(srcport));
+        return;
+    }
+    mDNSPlatformMemZero(pc, sizeof(DNSProxyClient));
+    pc->addr = *srcaddr;
+    pc->port = srcport;
+    pc->msgid = msg->h.id;
+    pc->interfaceID = InterfaceID; // input interface 
+    pc->socket = socket;
+    pc->tcp = tcp;
+    pc->requestFlags = msg->h.flags;
+    pc->context = context;
+    AssignDomainName(&pc->qname, &q.qname);
+    if (optRR)
+    {
+        if (!ParseEDNS0(pc, optRR, optLen, end))
+        {
+            LogInfo("ProxyCallbackCommon: Invalid EDNS0 option for pkt from %#a:%d, ignoring this", srcaddr, mDNSVal16(srcport));
+        }
+        else
+        {
+            pc->optRR = mDNSPlatformMemAllocate(optLen);
+            if (!pc->optRR)
+            {
+                LogMsg("ProxyCallbackCommon: Memory failure for pkt from %#a:%d, ignoring this", srcaddr, mDNSVal16(srcport));
+                FreeDNSProxyClient(pc);
+                return;
+            }
+            mDNSPlatformMemCopy(pc->optRR, optRR, optLen);
+            pc->optLen = optLen;
+        }
+    }
+
+    debugf("ProxyCallbackCommon: DNS Query forwarding to interface index %d", m->dp_opintf);
+    mDNS_SetupQuestion(&pc->q, (mDNSInterfaceID)(unsigned long)m->dp_opintf, &q.qname, q.qtype, ProxyClientCallback, pc);
+    pc->q.TimeoutQuestion = 1;
+    // Even though we don't care about intermediate responses, set ReturnIntermed so that
+    // we get the negative responses
+    pc->q.ReturnIntermed  = mDNStrue;
+    pc->q.ProxyQuestion   = mDNStrue;
+    pc->q.ProxyDNSSECOK   = pc->DNSSECOK;
+    pc->q.responseFlags   = zeroID;
+    if (pc->DNSSECOK)
+    {
+        if (!(msg->h.flags.b[1] & kDNSFlag1_CD) && pc->q.qtype != kDNSType_RRSIG && pc->q.qtype != kDNSQType_ANY)
+        {
+            LogInfo("ProxyCallbackCommon: Setting Validation required bit for %#a:%d, validating %##s (%s)", srcaddr, mDNSVal16(srcport),
+                q.qname.c, DNSTypeName(q.qtype));
+            pc->q.ValidationRequired = DNSSEC_VALIDATION_SECURE;
+        }
+        else
+        {
+            LogInfo("ProxyCallbackCommon: CD bit not set OR not a valid type for %#a:%d, not validating %##s (%s)", srcaddr, mDNSVal16(srcport),
+                q.qname.c, DNSTypeName(q.qtype));
+        }
+    }
+    else
+    {
+        LogInfo("ProxyCallbackCommon: DNSSEC OK bit not set for %#a:%d, not validating %##s (%s)", srcaddr, mDNSVal16(srcport),
+                q.qname.c, DNSTypeName(q.qtype));
+    }
+
+    while (*ppc)
+        ppc = &((*ppc)->next);
+    *ppc = pc;
+
+    mDNS_StartQuery(m, &pc->q);
+}
+
+mDNSexport void ProxyUDPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr,
+    const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context)
+{
+    LogInfo("ProxyUDPCallback: DNS Message from %#a:%d to %#a:%d length %d", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt);
+    ProxyCallbackCommon(m, socket, pkt, end, srcaddr, srcport, dstaddr, dstport, InterfaceID, mDNSfalse, context);
+}
+
+mDNSexport void ProxyTCPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr,
+    const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context)
+{
+    LogInfo("ProxyTCPCallback: DNS Message from %#a:%d to %#a:%d length %d", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt);
+    // If the connection was closed from the other side, locate the client
+    // state and free it.
+    if ((end - (mDNSu8 *)pkt) == 0)
+    {
+        DNSProxyClient **ppc = &DNSProxyClients;
+        DNSProxyClient **prevpc;
+
+        prevpc = ppc;
+        while (*ppc && (*ppc)->socket != socket)
+        {
+            prevpc = ppc;
+            ppc=&(*ppc)->next;
+        }
+        if (!*ppc)
+        {
+            mDNSPlatformDisposeProxyContext(socket);
+            LogMsg("ProxyTCPCallback: socket cannot be found");
+            return;
+        }
+        *prevpc = (*ppc)->next;
+        LogInfo("ProxyTCPCallback: free");
+        mDNSPlatformDisposeProxyContext(socket);
+        FreeDNSProxyClient(*ppc);
+        return;
+    }
+    ProxyCallbackCommon(m, socket, pkt, end, srcaddr, srcport, dstaddr, dstport, InterfaceID, mDNStrue, context);
+}
+
+mDNSexport void DNSProxyInit(mDNS *const m, mDNSu32 IpIfArr[MaxIp], mDNSu32 OpIf)
+{
+    int i;
+
+    // Store DNSProxy Interface fields in mDNS struct
+    for (i = 0; i < MaxIp; i++)
+        m->dp_ipintf[i]  = IpIfArr[i];
+    m->dp_opintf         = OpIf;
+
+    LogInfo("DNSProxyInit Storing interface list: Input [%d, %d, %d, %d, %d] Output [%d]", m->dp_ipintf[0],
+            m->dp_ipintf[1], m->dp_ipintf[2], m->dp_ipintf[3], m->dp_ipintf[4], m->dp_opintf);
+}
+
+mDNSexport void DNSProxyTerminate(mDNS *const m)
+{
+    int i;
+    
+    // Clear DNSProxy Interface fields from mDNS struct
+    for (i = 0; i < MaxIp; i++)
+        m->dp_ipintf[i]  = 0;
+    m->dp_opintf         = 0; 
+    
+    LogInfo("DNSProxyTerminate Cleared interface list: Input [%d, %d, %d, %d, %d] Output [%d]", m->dp_ipintf[0],
+            m->dp_ipintf[1], m->dp_ipintf[2], m->dp_ipintf[3], m->dp_ipintf[4], m->dp_opintf);
+}
+#else // UNICAST_DISABLED
+
+mDNSexport void ProxyUDPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context)
+{
+    (void) m;
+    (void) socket;
+    (void) pkt;
+    (void) end;
+    (void) srcaddr;
+    (void) srcport;
+    (void) dstaddr;
+    (void) dstport;
+    (void) InterfaceID;
+    (void) context;
+}
+
+mDNSexport void ProxyTCPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context)
+{
+    (void) m;
+    (void) socket;
+    (void) pkt;
+    (void) end;
+    (void) srcaddr;
+    (void) srcport;
+    (void) dstaddr;
+    (void) dstport;
+    (void) InterfaceID;
+    (void) context;
+}
+
+mDNSexport void DNSProxyInit(mDNS *const m, mDNSu32 IpIfArr[MaxIp], mDNSu32 OpIf)
+{
+    (void) m;
+    (void) IpIfArr;
+    (void) OpIf;
+}
+extern void DNSProxyTerminate(mDNS *const m)
+{
+    (void) m;
+}
+
+
+#endif // UNICAST_DISABLED
diff --git a/mDNSCore/dnsproxy.h b/mDNSCore/dnsproxy.h
new file mode 100644 (file)
index 0000000..ed46a12
--- /dev/null
@@ -0,0 +1,30 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2011 Apple Computer, 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 __DNS_PROXY_H
+#define __DNS_PROXY_H
+
+#include "mDNSEmbeddedAPI.h"
+#include "DNSCommon.h"
+
+extern void ProxyUDPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr,
+                             const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context);
+extern void ProxyTCPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr,
+                             const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context);                          
+extern void DNSProxyInit(mDNS *const m, mDNSu32 IpIfArr[MaxIp], mDNSu32 OpIf);
+extern void DNSProxyTerminate(mDNS *const m);
+
+#endif // __DNS_PROXY_H
index 5e582de15759bd850b2841c45350c420fcb2290e..c83b8413636ed4fb625c3a8d7fd1d6cba44b9226 100644 (file)
  * limitations under the License.
  */
 #include "mDNSEmbeddedAPI.h"
  * limitations under the License.
  */
 #include "mDNSEmbeddedAPI.h"
+#include "DNSSECSupport.h"
 #include "DNSCommon.h"
 #include "dnssec.h"
 #include "CryptoAlg.h"
 #include "nsec.h"
 #include "DNSCommon.h"
 #include "dnssec.h"
 #include "CryptoAlg.h"
 #include "nsec.h"
+#include "nsec3.h"
+
+// Define DNSSEC_DISABLED to remove all the DNSSEC functionality
+// and use the stub functions implemented later in this file.
+
+#ifndef DNSSEC_DISABLED
 
 //#define DNSSEC_DEBUG
 
 
 //#define DNSSEC_DEBUG
 
 // is done, DNSSECPositiveValidationCB or DNSSECNegativeValidationCB will be called which will then deliver the
 // validation results to the original question that started the validation.
 //
 // is done, DNSSECPositiveValidationCB or DNSSECNegativeValidationCB will be called which will then deliver the
 // validation results to the original question that started the validation.
 //
+// Insecure proofs are done when the verification ends up bogus. The flow would look like this
+//
+// VerifySignature -> StartDNSSECVerification - GetAllRRSetsForVerification -> FinishDNSSECVerification -> DNSSECValidationCB
+// {DNSSECPositiveValidationCB, DNSSECNegativeValidationCB} -> ProveInsecure -> VerifySignaure ->
+//
+// ProveInsecure finds the break in trust in a top-down fashion.
+//
 // Forward declaration
 mDNSlocal void VerifySigCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord);
 mDNSlocal mStatus TrustedKey(mDNS *const m, DNSSECVerifier *dv);
 mDNSlocal mDNSBool TrustedKeyPresent(mDNS *const m, DNSSECVerifier *dv);
 mDNSlocal mStatus ValidateDS(DNSSECVerifier *dv);
 // Forward declaration
 mDNSlocal void VerifySigCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord);
 mDNSlocal mStatus TrustedKey(mDNS *const m, DNSSECVerifier *dv);
 mDNSlocal mDNSBool TrustedKeyPresent(mDNS *const m, DNSSECVerifier *dv);
 mDNSlocal mStatus ValidateDS(DNSSECVerifier *dv);
-mDNSlocal void DNSSECNegativeValidationCB(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status);
+mDNSlocal void DNSSECNegativeValidationCB(mDNS *const m, DNSSECVerifier *dv, CacheGroup *cg, ResourceRecord *answer, DNSSECStatus status);
+mDNSlocal RRVerifier* CopyRRVerifier(RRVerifier *from);
 
 // Currently we use this to convert a RRVerifier to resource record so that we can
 // use the standard DNS utility functions
 
 // Currently we use this to convert a RRVerifier to resource record so that we can
 // use the standard DNS utility functions
@@ -80,6 +95,9 @@ LargeCacheRecord largerec;
 // removed in the future.
 #define MAX_RECURSE_COUNT   10
 
 // removed in the future.
 #define MAX_RECURSE_COUNT   10
 
+// TTL (in seconds) when the DNSSEC status is Bogus
+#define RR_BOGUS_TTL        60
+
 // RFC 4034 Appendix B: Get the keyid of a DNS KEY. It is not transmitted
 // explicitly on the wire.
 //
 // RFC 4034 Appendix B: Get the keyid of a DNS KEY. It is not transmitted
 // explicitly on the wire.
 //
@@ -103,7 +121,7 @@ mDNSlocal mDNSu32 keytag(mDNSu8 *key, mDNSu32 keysize)
     return ac & 0xFFFF;
 }
 
     return ac & 0xFFFF;
 }
 
-mDNSlocal int DNSMemCmp(mDNSu8 *const m1, mDNSu8 *const m2, int len)
+mDNSexport int DNSMemCmp(const mDNSu8 *const m1, const mDNSu8 *const m2, int len)
 {
     int res;
 
 {
     int res;
 
@@ -113,32 +131,96 @@ mDNSlocal int DNSMemCmp(mDNSu8 *const m1, mDNSu8 *const m2, int len)
     return 0;
 }
 
     return 0;
 }
 
-mDNSlocal mStatus DNSNameToLowerCase(domainname *d, domainname *result)
+// RFC 4034:
+//
+// Section 6.1:
+//
+// For the purposes of DNS security, owner names are ordered by treating
+// individual labels as unsigned left-justified octet strings.  The
+// absence of a octet sorts before a zero value octet, and uppercase
+// US-ASCII letters are treated as if they were lowercase US-ASCII
+// letters.
+//
+// To compute the canonical ordering of a set of DNS names, start by
+// sorting the names according to their most significant (rightmost)
+// labels.  For names in which the most significant label is identical,
+// continue sorting according to their next most significant label, and
+// so forth.
+//
+// Returns 0 if the names are same
+// Returns -1 if d1 < d2
+// Returns  1 if d1 > d2
+//
+// subdomain is set if there is at least one label match (starting from the end)
+// and d1 has more labels than d2 e.g., a.b.com is a subdomain of b.com
+//
+mDNSexport int DNSSECCanonicalOrder(const domainname *const d1, const domainname *const d2, int *subdomain)
 {
 {
-    const mDNSu8 *a = d->c;
-    mDNSu8 *b = result->c;
-    const mDNSu8 *const max = d->c + MAX_DOMAIN_NAME;
-    int i, len;
+    int count, c1, c2;
+    int i, skip1, skip2;
+
+    c1 = CountLabels(d1);
+    skip1 = c1 - 1;
+    c2 = CountLabels(d2);
+    skip2 = c2 - 1;
 
 
-    while (*a)
+    if (subdomain) *subdomain = 0;
+
+    // Compare as many labels as possible starting from the rightmost
+    count = c1 < c2 ? c1 : c2;
+    for (i = count; i > 0; i--)
     {
     {
-        if (a + 1 + *a >= max)
-        {
-            LogMsg("DNSNameToLowerCase: ERROR!! Malformed Domain name");
-            return mStatus_BadParamErr;
-        }
-        len = *a++;
-        *b++ = len;
-        for (i = 0; i < len; i++)
+        mDNSu8 *a, *b;
+        int j, len, lena, lenb;
+
+        a = (mDNSu8 *)SkipLeadingLabels(d1, skip1);
+        b = (mDNSu8 *)SkipLeadingLabels(d2, skip2);
+        lena = *a;
+        lenb = *b;
+        // Compare label by label. Note that "z" > "yak" because z > y, but z < za
+        // (lena - lenb check below) because 'za' has two characters. Hence compare the
+        // letters first and then compare the length of the label at the end.
+        len = lena < lenb ? lena : lenb;
+        a++; b++;
+        for (j = 0; j < len; j++)
         {
             mDNSu8 ac = *a++;
         {
             mDNSu8 ac = *a++;
+            mDNSu8 bc = *b++;
             if (mDNSIsUpperCase(ac)) ac += 'a' - 'A';
             if (mDNSIsUpperCase(ac)) ac += 'a' - 'A';
-            *b++ = ac;
+            if (mDNSIsUpperCase(bc)) bc += 'a' - 'A';
+            if (ac != bc)
+            {
+                verbosedebugf("DNSSECCanonicalOrder: returning ac %c, bc %c", ac, bc);
+                return ((ac < bc) ? -1 : 1);
+            }
+        }
+        if ((lena - lenb) != 0)
+        {
+            verbosedebugf("DNSSECCanonicalOrder: returning lena %d lenb %d", lena, lenb);
+            return ((lena < lenb) ? -1 : 1);
         }
         }
+
+        // Continue with the next label
+        skip1--;
+        skip2--;
     }
     }
-    *b = 0;
+    // We have compared label by label. Both of them are same if we are here.
+    //
+    // Two possibilities.
+    //
+    // 1) Both names have same number of labels. In that case, return zero.
+    // 2) The number of labels is not same. As zero label sorts before, names
+    //    with more number of labels is greater.
 
 
-    return mStatus_NoError;
+    // a.b.com is a subdomain of b.com
+    if ((c1 > c2) && subdomain)
+        *subdomain = 1;
+
+    verbosedebugf("DNSSECCanonicalOrder: returning c1 %d c2 %d\n", c1, c2);
+    if (c1 != c2)
+        return ((c1 < c2) ? -1 : 1);
+    else
+        return 0;
 }
 
 // Initialize the question enough so that it can be answered from the cache using SameNameRecordAnswersQuestion or
 }
 
 // Initialize the question enough so that it can be answered from the cache using SameNameRecordAnswersQuestion or
@@ -146,7 +228,7 @@ mDNSlocal mStatus DNSNameToLowerCase(domainname *d, domainname *result)
 mDNSexport void InitializeQuestion(mDNS *const m, DNSQuestion *question, mDNSInterfaceID InterfaceID, const domainname *qname,
                                    mDNSu16 qtype, mDNSQuestionCallback *callback, void *context)
 {
 mDNSexport void InitializeQuestion(mDNS *const m, DNSQuestion *question, mDNSInterfaceID InterfaceID, const domainname *qname,
                                    mDNSu16 qtype, mDNSQuestionCallback *callback, void *context)
 {
-    LogOperation("InitializeQuestion: Called for %##s (%s)", qname->c, DNSTypeName(qtype));
+    debugf("InitializeQuestion: Called for %##s (%s)", qname->c, DNSTypeName(qtype));
 
     if (question->ThisQInterval != -1) mDNS_StopQuery(m, question);
 
 
     if (question->ThisQInterval != -1) mDNS_StopQuery(m, question);
 
@@ -154,10 +236,13 @@ mDNSexport void InitializeQuestion(mDNS *const m, DNSQuestion *question, mDNSInt
     question->qnamehash  = DomainNameHashValue(qname);
     question->ValidatingResponse = mDNStrue;
 
     question->qnamehash  = DomainNameHashValue(qname);
     question->ValidatingResponse = mDNStrue;
 
+    // Need to hold the lock, as GetServerForQuestion (its callers) references m->timenow.
+    mDNS_Lock(m);
     // We need to set the DNS server appropriately to match the question against the cache record.
     // Though not all callers of this function need it, we always do it to keep it simple.
     SetValidDNSServers(m, question);
     question->qDNSServer = GetServerForQuestion(m, question);
     // We need to set the DNS server appropriately to match the question against the cache record.
     // Though not all callers of this function need it, we always do it to keep it simple.
     SetValidDNSServers(m, question);
     question->qDNSServer = GetServerForQuestion(m, question);
+    mDNS_Unlock(m);
 
     // Make it look like unicast
     question->TargetQID = onesID;
 
     // Make it look like unicast
     question->TargetQID = onesID;
@@ -168,7 +253,7 @@ mDNSexport void InitializeQuestion(mDNS *const m, DNSQuestion *question, mDNSInt
 }
 
 mDNSexport DNSSECVerifier *AllocateDNSSECVerifier(mDNS *const m, const domainname *name, mDNSu16 rrtype, mDNSInterfaceID InterfaceID,
 }
 
 mDNSexport DNSSECVerifier *AllocateDNSSECVerifier(mDNS *const m, const domainname *name, mDNSu16 rrtype, mDNSInterfaceID InterfaceID,
-                                                  DNSSECVerifierCallback dvcallback, mDNSQuestionCallback qcallback)
+    mDNSu8 ValidationRequired, DNSSECVerifierCallback dvcallback, mDNSQuestionCallback qcallback)
 {
     DNSSECVerifier *dv;
 
 {
     DNSSECVerifier *dv;
 
@@ -176,6 +261,8 @@ mDNSexport DNSSECVerifier *AllocateDNSSECVerifier(mDNS *const m, const domainnam
     if (!dv) { LogMsg("AllocateDNSSECVerifier: ERROR!! memory alloc failed"); return mDNSNULL; }
     mDNSPlatformMemZero(dv, sizeof(*dv));
 
     if (!dv) { LogMsg("AllocateDNSSECVerifier: ERROR!! memory alloc failed"); return mDNSNULL; }
     mDNSPlatformMemZero(dv, sizeof(*dv));
 
+    LogDNSSEC("AllocateDNSSECVerifier called %p", dv);
+
     // Remember the question's name and type so that when we are done processing all
     // the verifications, we can trace the original question back
     AssignDomainName(&dv->origName, name);
     // Remember the question's name and type so that when we are done processing all
     // the verifications, we can trace the original question back
     AssignDomainName(&dv->origName, name);
@@ -183,22 +270,87 @@ mDNSexport DNSSECVerifier *AllocateDNSSECVerifier(mDNS *const m, const domainnam
     dv->InterfaceID = InterfaceID;
     dv->DVCallback = dvcallback;
     dv->q.ThisQInterval = -1;
     dv->InterfaceID = InterfaceID;
     dv->DVCallback = dvcallback;
     dv->q.ThisQInterval = -1;
-    dv->ac = mDNSNULL;
-    dv->actail = &dv->ac;
+    ResetAuthChain(dv);
+    // These two are used for Insecure proof if we end up doing it.
+    // -Value of ValidationRequired so that we know whether this is a secure or insecure validation
+    // -InsecureProofDone tells us whether the proof has been done or not
+    dv->ValidationRequired = ValidationRequired;
+    dv->InsecureProofDone = 0;
+    dv->NumPackets = 0;
+    mDNS_Lock(m);
+    dv->StartTime = m->timenow;
+    mDNS_Unlock(m);
     // The verifier's question has to be initialized as some of the callers assume it
     InitializeQuestion(m, &dv->q, InterfaceID, name, rrtype, qcallback, dv);
     return dv;
 }
 
     // The verifier's question has to be initialized as some of the callers assume it
     InitializeQuestion(m, &dv->q, InterfaceID, name, rrtype, qcallback, dv);
     return dv;
 }
 
-mDNSlocal void FreeDNSSECAuthChain(DNSSECVerifier *dv)
+mDNSlocal AuthChain *AuthChainCopy(AuthChain *ae)
+{
+    RRVerifier *rvfrom, **rvto;
+    AuthChain **prev = mDNSNULL;
+    AuthChain *retac = mDNSNULL;
+    AuthChain *ac;
+
+
+    while (ae)
+    {
+        ac = mDNSPlatformMemAllocate(sizeof(AuthChain));
+        if (!ac)
+        {
+            LogMsg("AuthChainCopy: AuthChain alloc failure");
+            return mDNSfalse;
+        }
+
+        ac->next  = mDNSNULL;
+
+        if (!retac)
+            retac = ac;
+
+        rvfrom = ae->rrset;
+        rvto = &ac->rrset;
+        while (rvfrom)
+        {
+            *rvto = CopyRRVerifier(rvfrom);
+            rvfrom = rvfrom->next;
+            rvto = &((*rvto)->next);
+        }
+
+        rvfrom = ae->rrsig;
+        rvto = &ac->rrsig;
+        while (rvfrom)
+        {
+            *rvto = CopyRRVerifier(rvfrom);
+            rvfrom = rvfrom->next;
+            rvto = &((*rvto)->next);
+        }
+
+        rvfrom = ae->key;
+        rvto = &ac->key;
+        while (rvfrom)
+        {
+            *rvto = CopyRRVerifier(rvfrom);
+            rvfrom = rvfrom->next;
+            rvto = &((*rvto)->next);
+        }
+
+        if (prev)
+        {
+            *prev = ac;
+        }
+        prev = &(ac->next);
+        ae = ae->next;
+    }
+    return retac;
+}
+
+mDNSlocal void FreeDNSSECAuthChainInfo(AuthChain *ac)
 {
     RRVerifier *rrset;
     RRVerifier *next;
 {
     RRVerifier *rrset;
     RRVerifier *next;
-    AuthChain *ac, *acnext;
-
-    LogDNSSEC("FreeDNSSECAuthChain: called");
+    AuthChain *acnext;
 
 
-    ac = dv->ac;
+    LogDNSSEC("FreeDNSSECAuthChainInfo: called");
 
     while (ac)
     {
 
     while (ac)
     {
@@ -233,7 +385,29 @@ mDNSlocal void FreeDNSSECAuthChain(DNSSECVerifier *dv)
         mDNSPlatformMemFree(ac);
         ac = acnext;
     }
         mDNSPlatformMemFree(ac);
         ac = acnext;
     }
-    dv->ac = mDNSNULL;
+}
+
+mDNSlocal void FreeDNSSECAuthChain(DNSSECVerifier *dv)
+{
+    if (dv->ac)
+    {
+        FreeDNSSECAuthChainInfo(dv->ac);
+        // if someone reuses the "dv", it will be initialized properly
+        ResetAuthChain(dv);
+    }
+    if (dv->saveac)
+    {
+        FreeDNSSECAuthChainInfo(dv->saveac);
+        dv->saveac = mDNSNULL;
+    }
+}
+
+mDNSlocal void FreeAuthChain(mDNS *const m, void *context)
+{
+    AuthChain *ac = (AuthChain *)context;
+    (void) m; // unused
+
+    FreeDNSSECAuthChainInfo(ac);
 }
 
 mDNSlocal void FreeDNSSECVerifierRRSets(DNSSECVerifier *dv)
 }
 
 mDNSlocal void FreeDNSSECVerifierRRSets(DNSSECVerifier *dv)
@@ -286,20 +460,26 @@ mDNSlocal void FreeDNSSECVerifierRRSets(DNSSECVerifier *dv)
         rrset = next;
     }
     dv->ds = mDNSNULL;
         rrset = next;
     }
     dv->ds = mDNSNULL;
-    if (dv->pendingNSEC)
+    rrset = dv->pendingNSEC;
+    while (rrset)
     {
     {
-        mDNSPlatformMemFree(dv->pendingNSEC);
-        dv->pendingNSEC = mDNSNULL;
+        next = rrset->next;
+        mDNSPlatformMemFree(rrset);
+        rrset = next;
     }
     }
+    dv->pendingNSEC = mDNSNULL;
 }
 
 mDNSexport void FreeDNSSECVerifier(mDNS *const m, DNSSECVerifier *dv)
 {
     LogDNSSEC("FreeDNSSECVerifier called %p", dv);
 }
 
 mDNSexport void FreeDNSSECVerifier(mDNS *const m, DNSSECVerifier *dv)
 {
     LogDNSSEC("FreeDNSSECVerifier called %p", dv);
-    if (dv->q.ThisQInterval != -1) mDNS_StopQuery(m, &dv->q);
+    if (dv->q.ThisQInterval != -1)
+        mDNS_StopQuery(m, &dv->q);
     FreeDNSSECVerifierRRSets(dv);
     FreeDNSSECVerifierRRSets(dv);
-    if (dv->ctx) AlgDestroy(dv->ctx);
-    if (dv->ac) FreeDNSSECAuthChain(dv);
+    if (dv->ctx)
+        AlgDestroy(dv->ctx);
+    if (dv->ac || dv->saveac)
+        FreeDNSSECAuthChain(dv);
     if (dv->parent)
     {
         LogDNSSEC("FreeDNSSECVerifier freeing parent %p", dv->parent);
     if (dv->parent)
     {
         LogDNSSEC("FreeDNSSECVerifier freeing parent %p", dv->parent);
@@ -308,6 +488,23 @@ mDNSexport void FreeDNSSECVerifier(mDNS *const m, DNSSECVerifier *dv)
     mDNSPlatformMemFree(dv);
 }
 
     mDNSPlatformMemFree(dv);
 }
 
+mDNSlocal RRVerifier* CopyRRVerifier(RRVerifier *from)
+{
+    RRVerifier *r;
+
+    r = mDNSPlatformMemAllocate(sizeof (RRVerifier) + from->rdlength);
+    if (!r)
+    {
+        LogMsg("CopyRRVerifier: memory failure");
+        return mDNSNULL;
+    }
+    mDNSPlatformMemCopy(r, from, sizeof(RRVerifier));
+    r->next = mDNSNULL;
+    r->rdata = (mDNSu8*) ((mDNSu8 *)r + sizeof(RRVerifier));
+    mDNSPlatformMemCopy(r->rdata, from->rdata, r->rdlength);
+    return r;
+}
+
 mDNSexport RRVerifier* AllocateRRVerifier(const ResourceRecord *const rr, mStatus *status)
 {
     RRVerifier *r;
 mDNSexport RRVerifier* AllocateRRVerifier(const ResourceRecord *const rr, mStatus *status)
 {
     RRVerifier *r;
@@ -510,6 +707,7 @@ mDNSlocal mStatus CheckRRSIGForRRSet(mDNS *const m, DNSSECVerifier *dv, CacheRec
     CacheGroup *cg;
     CacheRecord *cr;
     RRVerifier *rv;
     CacheGroup *cg;
     CacheRecord *cr;
     RRVerifier *rv;
+    mDNSBool expectRRSIG = mDNSfalse;
 
     *negcr = mDNSNULL;
     if (!dv->rrset)
 
     *negcr = mDNSNULL;
     if (!dv->rrset)
@@ -531,19 +729,30 @@ mDNSlocal mStatus CheckRRSIGForRRSet(mDNS *const m, DNSSECVerifier *dv, CacheRec
     for (cr=cg->members; cr; cr=cr->next)
     {
         debugdnssec("CheckRRSIGForRRSet: checking the validity of rrsig");
     for (cr=cg->members; cr; cr=cr->next)
     {
         debugdnssec("CheckRRSIGForRRSet: checking the validity of rrsig");
-        if (cr->resrec.rrtype != kDNSType_RRSIG) continue;
+        if (cr->resrec.rrtype != kDNSType_RRSIG)
+        {
+            // Check to see if we should expect RRSIGs for the type that we are looking for.
+            // We would expect RRSIGs, if we had previously issued the question with the
+            // EDNS0/DOK bit set.
+            if (cr->resrec.rrtype == dv->rrset->rrtype)
+            {
+                expectRRSIG = cr->CRDNSSECQuestion;
+                LogDNSSEC("CheckRRSIGForRRSet: %s RRSIG for %s", (expectRRSIG ? "Expecting" : "Not Expecting"), CRDisplayString(m, cr));
+            }
+            continue;
+        }
         if (cr->resrec.RecordType == kDNSRecordTypePacketNegative)
         {
             if (!(*negcr))
             {
                 LogDNSSEC("CheckRRSIGForRRSet: Negative cache record %s encountered for %##s (%s)", CRDisplayString(m, cr),
         if (cr->resrec.RecordType == kDNSRecordTypePacketNegative)
         {
             if (!(*negcr))
             {
                 LogDNSSEC("CheckRRSIGForRRSet: Negative cache record %s encountered for %##s (%s)", CRDisplayString(m, cr),
-                          rv->name.c, rv->rrtype);
+                          rv->name.c, DNSTypeName(rv->rrtype));
                 *negcr = cr;
             }
             else
             {
                 LogMsg("CheckRRSIGForRRSet: ERROR!! Negative cache record %s already set for %##s (%s)", CRDisplayString(m, cr),
                 *negcr = cr;
             }
             else
             {
                 LogMsg("CheckRRSIGForRRSet: ERROR!! Negative cache record %s already set for %##s (%s)", CRDisplayString(m, cr),
-                       rv->name.c, rv->rrtype);
+                       rv->name.c, DNSTypeName(rv->rrtype));
             }
             continue;
         }
             }
             continue;
         }
@@ -553,11 +762,16 @@ mDNSlocal mStatus CheckRRSIGForRRSet(mDNS *const m, DNSSECVerifier *dv, CacheRec
     {
         // Encountered both RRSIG and negative CR
         LogMsg("CheckRRSIGForRRSet: ERROR!! Encountered negative cache record %s and RRSIG for %##s (%s)",
     {
         // Encountered both RRSIG and negative CR
         LogMsg("CheckRRSIGForRRSet: ERROR!! Encountered negative cache record %s and RRSIG for %##s (%s)",
-               CRDisplayString(m, *negcr), rv->name.c, rv->rrtype);
+               CRDisplayString(m, *negcr), rv->name.c, DNSTypeName(rv->rrtype));
         return mStatus_BadParamErr;
     }
         return mStatus_BadParamErr;
     }
+    // If we can't find RRSIGs, but we find a negative response then we need to validate that
+    // which the caller will do it. Otherwise, if we should be expecting RRSIGs to be in the
+    // cache already, then return error.
     if (dv->rrsig || *negcr)
         return mStatus_NoError;
     if (dv->rrsig || *negcr)
         return mStatus_NoError;
+    else if (expectRRSIG)
+        return mStatus_BadParamErr;
     else
         return mStatus_NoSuchRecord;
 }
     else
         return mStatus_NoSuchRecord;
 }
@@ -675,6 +889,7 @@ mDNSlocal mStatus CheckRRSIGForKey(mDNS *const m, DNSSECVerifier *dv, CacheRecor
     CacheRecord *cr;
     rdataRRSig *rrsig;
     domainname *name;
     CacheRecord *cr;
     rdataRRSig *rrsig;
     domainname *name;
+    mDNSBool expectRRSIG = mDNSfalse;
 
     *negcr = mDNSNULL;
     if (!dv->rrsig)
 
     *negcr = mDNSNULL;
     if (!dv->rrsig)
@@ -700,7 +915,18 @@ mDNSlocal mStatus CheckRRSIGForKey(mDNS *const m, DNSSECVerifier *dv, CacheRecor
     }
     for (cr=cg->members; cr; cr=cr->next)
     {
     }
     for (cr=cg->members; cr; cr=cr->next)
     {
-        if (cr->resrec.rrtype != kDNSType_RRSIG) continue;
+        if (cr->resrec.rrtype != kDNSType_RRSIG)
+        {
+            // Check to see if we should expect RRSIGs for the DNSKEY record that we are
+            // looking for. We would expect RRSIGs, if we had previously issued the question
+            // with the EDNS0/DOK bit set.
+            if (cr->resrec.rrtype == kDNSType_DNSKEY)
+            {
+                expectRRSIG = cr->CRDNSSECQuestion;
+                LogDNSSEC("CheckRRSIGForKey: %s RRSIG for %s", (expectRRSIG ? "Expecting" : "Not Expecting"), CRDisplayString(m, cr));
+            }
+            continue;
+        }
         if (cr->resrec.RecordType == kDNSRecordTypePacketNegative)
         {
             if (!(*negcr))
         if (cr->resrec.RecordType == kDNSRecordTypePacketNegative)
         {
             if (!(*negcr))
@@ -726,8 +952,12 @@ mDNSlocal mStatus CheckRRSIGForKey(mDNS *const m, DNSSECVerifier *dv, CacheRecor
                CRDisplayString(m, *negcr), name->c);
         return mStatus_BadParamErr;
     }
                CRDisplayString(m, *negcr), name->c);
         return mStatus_BadParamErr;
     }
+    // If we can't find RRSIGs, but we find a negative response then we need to validate that
+    // which the caller will do it. Finally, make sure that we are not expecting RRSIGS.
     if (dv->rrsigKey || *negcr)
         return mStatus_NoError;
     if (dv->rrsigKey || *negcr)
         return mStatus_NoError;
+    else if (expectRRSIG)
+        return mStatus_BadParamErr;
     else
         return mStatus_NoSuchRecord;
 }
     else
         return mStatus_NoSuchRecord;
 }
@@ -848,7 +1078,7 @@ mDNSlocal mDNSBool GetAllRRSetsForVerification(mDNS *const m, DNSSECVerifier *dv
     if (!dv->rrset)
     {
         LogMsg("GetAllRRSetsForVerification: ERROR!! rrset NULL");
     if (!dv->rrset)
     {
         LogMsg("GetAllRRSetsForVerification: ERROR!! rrset NULL");
-        dv->DVCallback(m, dv, DNSSEC_Indeterminate);
+        dv->DVCallback(m, dv, DNSSEC_Bogus);
         return mDNSfalse;
     }
 
         return mDNSfalse;
     }
 
@@ -866,7 +1096,7 @@ mDNSlocal mDNSBool GetAllRRSetsForVerification(mDNS *const m, DNSSECVerifier *dv
         err = CheckRRSIGForRRSet(m, dv, &negcr);
         if (err != mStatus_NoSuchRecord && err != mStatus_NoError)
         {
         err = CheckRRSIGForRRSet(m, dv, &negcr);
         if (err != mStatus_NoSuchRecord && err != mStatus_NoError)
         {
-            dv->DVCallback(m, dv, DNSSEC_Indeterminate);
+            dv->DVCallback(m, dv, DNSSEC_Bogus);
             return mDNSfalse;
         }
         // Need to initialize the question as if we end up in ValidateWithNSECS below, the nsec proofs
             return mDNSfalse;
         }
         // Need to initialize the question as if we end up in ValidateWithNSECS below, the nsec proofs
@@ -877,7 +1107,6 @@ mDNSlocal mDNSBool GetAllRRSetsForVerification(mDNS *const m, DNSSECVerifier *dv
         // We may not have the NSECS if the previous query was a non-DNSSEC query
         if (negcr && negcr->nsec)
         {
         // We may not have the NSECS if the previous query was a non-DNSSEC query
         if (negcr && negcr->nsec)
         {
-            dv->DVCallback = DNSSECNegativeValidationCB;
             ValidateWithNSECS(m, dv, negcr);
             return mDNSfalse;
         }
             ValidateWithNSECS(m, dv, negcr);
             return mDNSfalse;
         }
@@ -900,15 +1129,16 @@ mDNSlocal mDNSBool GetAllRRSetsForVerification(mDNS *const m, DNSSECVerifier *dv
             // Note: For this to work, the core needs to deliver RRSIGs when they are added to the cache even if the
             // "qtype" is not RRSIG.
             debugdnssec("GetAllRRSetsForVerification: Fetching RRSIGS for RRSET");
             // Note: For this to work, the core needs to deliver RRSIGs when they are added to the cache even if the
             // "qtype" is not RRSIG.
             debugdnssec("GetAllRRSetsForVerification: Fetching RRSIGS for RRSET");
+            dv->NumPackets++;
             mDNS_StartQuery(m, &dv->q);
             return mDNSfalse;
         }
             mDNS_StartQuery(m, &dv->q);
             return mDNSfalse;
         }
-    // if we found the RRSIG, then fall through to find the DNSKEY
+        // if we found the RRSIG, then fall through to find the DNSKEY
     case RRVS_key:
         err = CheckKeyForRRSIG(m, dv, &negcr);
         if (err != mStatus_NoSuchRecord && err != mStatus_NoError)
         {
     case RRVS_key:
         err = CheckKeyForRRSIG(m, dv, &negcr);
         if (err != mStatus_NoSuchRecord && err != mStatus_NoError)
         {
-            dv->DVCallback(m, dv, DNSSEC_Indeterminate);
+            dv->DVCallback(m, dv, DNSSEC_Bogus);
             return mDNSfalse;
         }
         // Need to initialize the question as if we end up in ValidateWithNSECS below, the nsec proofs
             return mDNSfalse;
         }
         // Need to initialize the question as if we end up in ValidateWithNSECS below, the nsec proofs
@@ -918,7 +1148,6 @@ mDNSlocal mDNSBool GetAllRRSetsForVerification(mDNS *const m, DNSSECVerifier *dv
         // We may not have the NSECS if the previous query was a non-DNSSEC query
         if (negcr && negcr->nsec)
         {
         // We may not have the NSECS if the previous query was a non-DNSSEC query
         if (negcr && negcr->nsec)
         {
-            dv->DVCallback = DNSSECNegativeValidationCB;
             ValidateWithNSECS(m, dv, negcr);
             return mDNSfalse;
         }
             ValidateWithNSECS(m, dv, negcr);
             return mDNSfalse;
         }
@@ -927,6 +1156,7 @@ mDNSlocal mDNSBool GetAllRRSetsForVerification(mDNS *const m, DNSSECVerifier *dv
         if (!dv->key)
         {
             debugdnssec("GetAllRRSetsForVerification: Fetching DNSKEY for RRSET");
         if (!dv->key)
         {
             debugdnssec("GetAllRRSetsForVerification: Fetching DNSKEY for RRSET");
+            dv->NumPackets++;
             mDNS_StartQuery(m, &dv->q);
             return mDNSfalse;
         }
             mDNS_StartQuery(m, &dv->q);
             return mDNSfalse;
         }
@@ -936,7 +1166,7 @@ mDNSlocal mDNSBool GetAllRRSetsForVerification(mDNS *const m, DNSSECVerifier *dv
         // if we are falling through, then it is okay if we don't find the record
         if (err != mStatus_NoSuchRecord && err != mStatus_NoError)
         {
         // if we are falling through, then it is okay if we don't find the record
         if (err != mStatus_NoSuchRecord && err != mStatus_NoError)
         {
-            dv->DVCallback(m, dv, DNSSEC_Indeterminate);
+            dv->DVCallback(m, dv, DNSSEC_Bogus);
             return mDNSfalse;
         }
         // Need to initialize the question as if we end up in ValidateWithNSECS below, the nsec proofs
             return mDNSfalse;
         }
         // Need to initialize the question as if we end up in ValidateWithNSECS below, the nsec proofs
@@ -946,15 +1176,15 @@ mDNSlocal mDNSBool GetAllRRSetsForVerification(mDNS *const m, DNSSECVerifier *dv
         // We may not have the NSECS if the previous query was a non-DNSSEC query
         if (negcr && negcr->nsec)
         {
         // We may not have the NSECS if the previous query was a non-DNSSEC query
         if (negcr && negcr->nsec)
         {
-            dv->DVCallback = DNSSECNegativeValidationCB;
             ValidateWithNSECS(m, dv, negcr);
             return mDNSfalse;
         }
         dv->next = RRVS_ds;
             ValidateWithNSECS(m, dv, negcr);
             return mDNSfalse;
         }
         dv->next = RRVS_ds;
-        debugdnssec("VerifySigCallback: RRVS_rrsig_key %p", dv->rrsigKey);
+        debugdnssec("GetAllRRSetsForVerification: RRVS_rrsig_key %p", dv->rrsigKey);
         if (!dv->rrsigKey)
         {
             debugdnssec("GetAllRRSetsForVerification: Fetching RRSIGS for DNSKEY");
         if (!dv->rrsigKey)
         {
             debugdnssec("GetAllRRSetsForVerification: Fetching RRSIGS for DNSKEY");
+            dv->NumPackets++;
             mDNS_StartQuery(m, &dv->q);
             return mDNSfalse;
         }
             mDNS_StartQuery(m, &dv->q);
             return mDNSfalse;
         }
@@ -968,7 +1198,7 @@ mDNSlocal mDNSBool GetAllRRSetsForVerification(mDNS *const m, DNSSECVerifier *dv
         err = CheckDSForKey(m, dv, &negcr);
         if (err != mStatus_NoSuchRecord && err != mStatus_NoError)
         {
         err = CheckDSForKey(m, dv, &negcr);
         if (err != mStatus_NoSuchRecord && err != mStatus_NoError)
         {
-            dv->DVCallback(m, dv, DNSSEC_Indeterminate);
+            dv->DVCallback(m, dv, DNSSEC_Bogus);
             return mDNSfalse;
         }
         // Need to initialize the question as if we end up in ValidateWithNSECS below, the nsec proofs
             return mDNSfalse;
         }
         // Need to initialize the question as if we end up in ValidateWithNSECS below, the nsec proofs
@@ -977,7 +1207,6 @@ mDNSlocal mDNSBool GetAllRRSetsForVerification(mDNS *const m, DNSSECVerifier *dv
         // We may not have the NSECS if the previous query was a non-DNSSEC query
         if (negcr && negcr->nsec)
         {
         // We may not have the NSECS if the previous query was a non-DNSSEC query
         if (negcr && negcr->nsec)
         {
-            dv->DVCallback = DNSSECNegativeValidationCB;
             ValidateWithNSECS(m, dv, negcr);
             return mDNSfalse;
         }
             ValidateWithNSECS(m, dv, negcr);
             return mDNSfalse;
         }
@@ -994,6 +1223,7 @@ mDNSlocal mDNSBool GetAllRRSetsForVerification(mDNS *const m, DNSSECVerifier *dv
                 return mDNSfalse;
             }
             debugdnssec("GetAllRRSetsForVerification: Fetching DS");
                 return mDNSfalse;
             }
             debugdnssec("GetAllRRSetsForVerification: Fetching DS");
+            dv->NumPackets++;
             mDNS_StartQuery(m, &dv->q);
             return mDNSfalse;
         }
             mDNS_StartQuery(m, &dv->q);
             return mDNSfalse;
         }
@@ -1696,10 +1926,10 @@ mDNSlocal mDNSBool ValidateSignatureWithKey(DNSSECVerifier *dv, RRVerifier *rrse
     dv->ctx = AlgCreate(CRYPTO_ALG, rrsig->alg);
     if (!dv->ctx)
     {
     dv->ctx = AlgCreate(CRYPTO_ALG, rrsig->alg);
     if (!dv->ctx)
     {
-        LogMsg("ValidateSignatureWithKey: ERROR!! No algorithm support for %d", rrsig->alg);
+        LogDNSSEC("ValidateSignatureWithKey: ERROR!! No algorithm support for %d", rrsig->alg);
         return mDNSfalse;
     }
         return mDNSfalse;
     }
-    AlgAdd(dv->ctx, (mDNSu8 *)rrsig, RRSIG_FIXED_SIZE);
+    AlgAdd(dv->ctx, (const mDNSu8 *)rrsig, RRSIG_FIXED_SIZE);
     AlgAdd(dv->ctx, signerName.c, sigNameLen);
 
     if (labels - rrsig->labels > 0)
     AlgAdd(dv->ctx, signerName.c, sigNameLen);
 
     if (labels - rrsig->labels > 0)
@@ -1756,7 +1986,9 @@ mDNSlocal mDNSBool ValidateSignatureWithKey(DNSSECVerifier *dv, RRVerifier *rrse
             {
                 ptr->rdata = mDNSPlatformMemAllocate(ptr->rdlength);
                 if (ptr->rdata)
             {
                 ptr->rdata = mDNSPlatformMemAllocate(ptr->rdlength);
                 if (ptr->rdata)
+                {
                     mDNSPlatformMemCopy(ptr->rdata, tmp->rdata, tmp->rdlength);
                     mDNSPlatformMemCopy(ptr->rdata, tmp->rdata, tmp->rdlength);
+                }
                 else
                 {
                     for (i = 0; i < nrrsets; i++)
                 else
                 {
                     for (i = 0; i < nrrsets; i++)
@@ -1791,16 +2023,16 @@ mDNSlocal mDNSBool ValidateSignatureWithKey(DNSSECVerifier *dv, RRVerifier *rrse
         }
 
         // Add the fixed part
         }
 
         // Add the fixed part
-        AlgAdd(dv->ctx, fixedPart, fixedPartLen);
+        AlgAdd(dv->ctx, (const mDNSu8 *)fixedPart, fixedPartLen);
 
         // Add the rdlength
         rdlen = swap16(p->rdlength);
 
         // Add the rdlength
         rdlen = swap16(p->rdlength);
-        AlgAdd(dv->ctx, (mDNSu8 *)&rdlen, sizeof(mDNSu16));
+        AlgAdd(dv->ctx, (const mDNSu8 *)&rdlen, sizeof(mDNSu16));
 
         ConvertRDATAToCanonical(p->rrtype, p->rdlength, p->rdata);
 
         PrintVarSignInfo(rdlen, p->rdata);
 
         ConvertRDATAToCanonical(p->rrtype, p->rdlength, p->rdata);
 
         PrintVarSignInfo(rdlen, p->rdata);
-        AlgAdd(dv->ctx, p->rdata, p->rdlength);
+        AlgAdd(dv->ctx, (const mDNSu8 *)p->rdata, p->rdlength);
     }
     // free the memory as we don't need it anymore
     for (i = 0; i < nrrsets; i++)
     }
     // free the memory as we don't need it anymore
     for (i = 0; i < nrrsets; i++)
@@ -1960,7 +2192,7 @@ mDNSlocal mStatus ValidateDS(DNSSECVerifier *dv)
             digestLen = dsv->rdlength - DS_FIXED_SIZE;
 
             AlgAdd(dv->ctx, name.c, DomainNameLength(&name));
             digestLen = dsv->rdlength - DS_FIXED_SIZE;
 
             AlgAdd(dv->ctx, name.c, DomainNameLength(&name));
-            AlgAdd(dv->ctx, key, keyv->rdlength);
+            AlgAdd(dv->ctx, (const mDNSu8 *)key, keyv->rdlength);
 
             algRet = AlgVerify(dv->ctx, mDNSNULL, 0, digest, digestLen);
             AlgDestroy(dv->ctx);
 
             algRet = AlgVerify(dv->ctx, mDNSNULL, 0, digest, digestLen);
             AlgDestroy(dv->ctx);
@@ -2099,7 +2331,7 @@ mDNSlocal void SetTTLRRSet(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus statu
 {
     DNSQuestion question;
     CacheRecord *rr;
 {
     DNSQuestion question;
     CacheRecord *rr;
-    RRVerifier *rv;
+    RRVerifier *rrsigv;
     rdataRRSig *rrsig;
     mDNSu32 slot;
     CacheGroup *cg;
     rdataRRSig *rrsig;
     mDNSu32 slot;
     CacheGroup *cg;
@@ -2109,25 +2341,27 @@ mDNSlocal void SetTTLRRSet(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus statu
     domainname *qname;
     mDNSu16 qtype;
     CacheRecord *rrsigRR;
     domainname *qname;
     mDNSu16 qtype;
     CacheRecord *rrsigRR;
+    mDNSs32 now;
 
     debugdnssec("SetTTLRRSet called");
 
 
     debugdnssec("SetTTLRRSet called");
 
-    // TBD: Just handle secure for now
-    if (status != DNSSEC_Secure) return;
-
-    // check to make sure we built a AuthChain as part of verification
-    if (!dv->ac || !dv->ac->rrset || !dv->ac->rrsig || !dv->ac->key)
+    if (status == DNSSEC_Insecure || status == DNSSEC_Indeterminate)
     {
     {
-        LogMsg("SetTTLRRSet: ERROR!! NULL element in chain");
-        FreeDNSSECVerifier(m, dv);
+        LogDNSSEC("SetTTLRRSET: not setting ttl for status %s", DNSSECStatusName(status));
         return;
     }
 
         return;
     }
 
+    mDNS_Lock(m);
+    now = m->timenow;
+    mDNS_Unlock(m);
+
     mDNSPlatformMemZero(&question, sizeof(DNSQuestion));
     rrTTL = rrsigTTL = rrsigOrigTTL = rrsigTimeTTL = 0;
 
     // 1. Locate the rrset name and get its TTL (take the first one as a representative
     mDNSPlatformMemZero(&question, sizeof(DNSQuestion));
     rrTTL = rrsigTTL = rrsigOrigTTL = rrsigTimeTTL = 0;
 
     // 1. Locate the rrset name and get its TTL (take the first one as a representative
-    // of the rrset).
+    // of the rrset). Ideally, we should set the TTL on the first validation. Instead,
+    // we do it whenever we validate which happens whenever a ValidationRequired question
+    // finishes validation. 
     qname = &dv->origName;
     qtype = dv->origType;
 
     qname = &dv->origName;
     qtype = dv->origType;
 
@@ -2136,11 +2370,18 @@ mDNSlocal void SetTTLRRSet(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus statu
     slot = HashSlot(&question.qname);
     cg = CacheGroupForName(m, slot, question.qnamehash, &question.qname);
 
     slot = HashSlot(&question.qname);
     cg = CacheGroupForName(m, slot, question.qnamehash, &question.qname);
 
-    if (!cg) { LogMsg("SetTTLRRSet cg NULL"); return; }
+    if (!cg)
+    {
+        LogMsg("SetTTLRRSet cg NULL for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType));
+        return;
+    }
+
     for (rr = cg->members; rr; rr = rr->next)
         if (SameNameRecordAnswersQuestion(&rr->resrec, &question))
         {
     for (rr = cg->members; rr; rr = rr->next)
         if (SameNameRecordAnswersQuestion(&rr->resrec, &question))
         {
-            rrTTL = rr->resrec.rroriginalttl;
+            // originalttl is never touched. The actual TTL is derived based on when it was
+            // received.
+            rrTTL = rr->resrec.rroriginalttl - (now - rr->TimeRcvd)/mDNSPlatformOneSecond;
             break;
         }
 
             break;
         }
 
@@ -2155,63 +2396,75 @@ mDNSlocal void SetTTLRRSet(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus statu
     // 2. Get the RRSIG ttl. For NSEC records we need to get the NSEC record's TTL as
     // the negative cache record that we created may not be right.
 
     // 2. Get the RRSIG ttl. For NSEC records we need to get the NSEC record's TTL as
     // the negative cache record that we created may not be right.
 
-    rv = dv->ac->rrsig;
-    rrsig = (rdataRRSig *)rv->rdata;
-    sigNameLen = DomainNameLength((domainname *)&rrsig->signerName);
-    // pointer to signature and the length
-    ptr = (mDNSu8 *)(rv->rdata + sigNameLen + RRSIG_FIXED_SIZE);
-    len = rv->rdlength - RRSIG_FIXED_SIZE - sigNameLen;
+    if (dv->ac && dv->ac->rrsig)
+    {
+        rrsigv = dv->ac->rrsig;
+        rrsig = (rdataRRSig *)rrsigv->rdata;
+        sigNameLen = DomainNameLength((domainname *)&rrsig->signerName);
+        // pointer to signature and the length
+        ptr = (mDNSu8 *)(rrsigv->rdata + sigNameLen + RRSIG_FIXED_SIZE);
+        len = rrsigv->rdlength - RRSIG_FIXED_SIZE - sigNameLen;
+    }
+    else
+    {
+        rrsigv = mDNSNULL;
+        rrsig = mDNSNULL;
+        ptr = mDNSNULL;
+        sigNameLen = len = 0;
+    }
 
     rrsigRR = mDNSNULL;
 
     rrsigRR = mDNSNULL;
-    if (rr->resrec.RecordType == kDNSRecordTypePacketNegative)
+    if (rr->resrec.RecordType == kDNSRecordTypePacketNegative && status == DNSSEC_Secure)
     {
         CacheRecord *ncr;
         rrTTL = 0;
         for (ncr = rr->nsec; ncr; ncr = ncr->next)
         {
     {
         CacheRecord *ncr;
         rrTTL = 0;
         for (ncr = rr->nsec; ncr; ncr = ncr->next)
         {
-            if (ncr->resrec.rrtype == kDNSType_NSEC)
+            if (ncr->resrec.rrtype == kDNSType_NSEC || ncr->resrec.rrtype == kDNSType_NSEC3)
             {
             {
-                rrTTL = ncr->resrec.rroriginalttl;
+                rrTTL = ncr->resrec.rroriginalttl - (now - ncr->TimeRcvd)/mDNSPlatformOneSecond;
                 debugdnssec("SetTTLRRSet: NSEC TTL %u", rrTTL);
             }
             // Note: we can't use dv->origName here as the NSEC record's RRSIG may not match
             // the original name
                 debugdnssec("SetTTLRRSet: NSEC TTL %u", rrTTL);
             }
             // Note: we can't use dv->origName here as the NSEC record's RRSIG may not match
             // the original name
-            if (ncr->resrec.rrtype == kDNSType_RRSIG && SameDomainName(ncr->resrec.name, &rv->name))
+            if (rrsigv && ncr->resrec.rrtype == kDNSType_RRSIG && SameDomainName(ncr->resrec.name, &rrsigv->name))
             {
                 RDataBody2 *rdb = (RDataBody2 *)ncr->resrec.rdata->u.data;
                 rdataRRSig *sig = (rdataRRSig *)rdb->data;
             {
                 RDataBody2 *rdb = (RDataBody2 *)ncr->resrec.rdata->u.data;
                 rdataRRSig *sig = (rdataRRSig *)rdb->data;
-                if (rv->rdlength != ncr->resrec.rdlength)
+                if (rrsigv->rdlength != ncr->resrec.rdlength)
                 {
                     debugdnssec("SetTTLRRSet length mismatch");
                     continue;
                 }
                 {
                     debugdnssec("SetTTLRRSet length mismatch");
                     continue;
                 }
-                if (mDNSPlatformMemSame(sig, rrsig, rv->rdlength))
+                if (mDNSPlatformMemSame(sig, rrsig, rrsigv->rdlength))
                 {
                 {
-                    rrsigTTL = ncr->resrec.rroriginalttl;
-                    rrsigOrigTTL = swap32(rrsig->origTTL);
+                    mDNSu32 remain = (now - ncr->TimeRcvd)/mDNSPlatformOneSecond;
+                    rrsigTTL = ncr->resrec.rroriginalttl - remain;
+                    rrsigOrigTTL = swap32(rrsig->origTTL) - remain;
                     rrsigTimeTTL = swap32(rrsig->sigExpireTime) - swap32(rrsig->sigInceptTime);
                 }
             }
                     rrsigTimeTTL = swap32(rrsig->sigExpireTime) - swap32(rrsig->sigInceptTime);
                 }
             }
-            if (rrTTL && rrsigTTL) break;
+            if (rrTTL && (!rrsigv || rrsigTTL)) break;
         }
     }
         }
     }
-    else
+    else if (rrsigv)
     {
         // Look for the matching RRSIG so that we can get its TTL
         for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
     {
         // Look for the matching RRSIG so that we can get its TTL
         for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
-            if (rr->resrec.rrtype == kDNSType_RRSIG && SameDomainName(rr->resrec.name, &rv->name))
+            if (rr->resrec.rrtype == kDNSType_RRSIG && SameDomainName(rr->resrec.name, &rrsigv->name))
             {
                 RDataBody2 *rdb = (RDataBody2 *)rr->resrec.rdata->u.data;
                 rdataRRSig *sig = (rdataRRSig *)rdb->data;
             {
                 RDataBody2 *rdb = (RDataBody2 *)rr->resrec.rdata->u.data;
                 rdataRRSig *sig = (rdataRRSig *)rdb->data;
-                if (rv->rdlength != rr->resrec.rdlength)
+                if (rrsigv->rdlength != rr->resrec.rdlength)
                 {
                     debugdnssec("SetTTLRRSet length mismatch");
                     continue;
                 }
                 {
                     debugdnssec("SetTTLRRSet length mismatch");
                     continue;
                 }
-                if (mDNSPlatformMemSame(sig, rrsig, rv->rdlength))
+                if (mDNSPlatformMemSame(sig, rrsig, rrsigv->rdlength))
                 {
                 {
-                    rrsigTTL = rr->resrec.rroriginalttl;
-                    rrsigOrigTTL = swap32(rrsig->origTTL);
+                    mDNSu32 remain = (now - rr->TimeRcvd)/mDNSPlatformOneSecond;
+                    rrsigTTL = rr->resrec.rroriginalttl - remain;
+                    rrsigOrigTTL = swap32(rrsig->origTTL) - remain;
                     rrsigTimeTTL = swap32(rrsig->sigExpireTime) - swap32(rrsig->sigInceptTime);
                     rrsigRR = rr;
                     break;
                     rrsigTimeTTL = swap32(rrsig->sigExpireTime) - swap32(rrsig->sigInceptTime);
                     rrsigRR = rr;
                     break;
@@ -2219,29 +2472,41 @@ mDNSlocal void SetTTLRRSet(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus statu
             }
     }
 
             }
     }
 
-    if (!rrTTL || !rrsigTTL || !rrsigOrigTTL || !rrsigTimeTTL)
+    // It is possible that there are no RRSIGs and in that case it is not an error
+    // to find the rrsigTTL.
+    if (!rrTTL || (rrsigv && (!rrsigTTL || !rrsigOrigTTL || !rrsigTimeTTL)))
     {
     {
-        LogMsg("SetTTLRRSet: ERROR!! Bad TTL rrtl %u, rrsigTTL %u, rrsigOrigTTL %u, rrsigTimeTTL %u for %##s (%s)",
+        LogDNSSEC("SetTTLRRSet: ERROR!! Bad TTL rrtl %u, rrsigTTL %u, rrsigOrigTTL %u, rrsigTimeTTL %u for %##s (%s)",
                rrTTL, rrsigTTL, rrsigOrigTTL, rrsigTimeTTL, qname->c, DNSTypeName(qtype));
         return;
     }
                rrTTL, rrsigTTL, rrsigOrigTTL, rrsigTimeTTL, qname->c, DNSTypeName(qtype));
         return;
     }
-    else
+    LogDNSSEC("SetTTLRRSet: TTL rrtl %u, rrsigTTL %u, rrsigOrigTTL %u, rrsigTimeTTL %u for %##s (%s)",
+        rrTTL, rrsigTTL, rrsigOrigTTL, rrsigTimeTTL, qname->c, DNSTypeName(qtype));
+
+    if (status == DNSSEC_Bogus)
     {
     {
-        LogDNSSEC("SetTTLRRSet: TTL rrtl %u, rrsigTTL %u, rrsigOrigTTL %u, rrsigTimeTTL %u for %##s (%s)",
-                  rrTTL, rrsigTTL, rrsigOrigTTL, rrsigTimeTTL, qname->c, DNSTypeName(qtype));
+        rrTTL = RR_BOGUS_TTL;
+        LogDNSSEC("SetTTLRRSet: setting to bogus TTL %d", rrTTL);
     }
 
     }
 
-    if (rrsigTTL < rrTTL)
-        rrTTL = rrsigTTL;
-    if (rrsigOrigTTL < rrTTL)
-        rrTTL = rrsigOrigTTL;
-    if (rrsigTimeTTL < rrTTL)
-        rrTTL = rrsigTimeTTL;
+    if (rrsigv)
+    {
+        if (rrsigTTL < rrTTL)
+            rrTTL = rrsigTTL;
+        if (rrsigOrigTTL < rrTTL)
+            rrTTL = rrsigOrigTTL;
+        if (rrsigTimeTTL < rrTTL)
+            rrTTL = rrsigTimeTTL;
+    }
 
     // Set the rrsig's TTL. For NSEC records, rrsigRR is NULL which means it expires when
     // the negative cache record expires.
     if (rrsigRR)
 
     // Set the rrsig's TTL. For NSEC records, rrsigRR is NULL which means it expires when
     // the negative cache record expires.
     if (rrsigRR)
+    {
         rrsigRR->resrec.rroriginalttl = rrTTL;
         rrsigRR->resrec.rroriginalttl = rrTTL;
+        rrsigRR->TimeRcvd = now;
+        rrsigRR->UnansweredQueries = 0;
+    }
 
     // Find the RRset and set its TTL
     for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
 
     // Find the RRset and set its TTL
     for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
@@ -2251,6 +2516,8 @@ mDNSlocal void SetTTLRRSet(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus statu
             LogDNSSEC("SetTTLRRSet: Setting the TTL %d for %s, question %##s (%s)", rrTTL, CRDisplayString(m, rr),
                       question.qname.c, DNSTypeName(rr->resrec.rrtype));
             rr->resrec.rroriginalttl = rrTTL;
             LogDNSSEC("SetTTLRRSet: Setting the TTL %d for %s, question %##s (%s)", rrTTL, CRDisplayString(m, rr),
                       question.qname.c, DNSTypeName(rr->resrec.rrtype));
             rr->resrec.rroriginalttl = rrTTL;
+            rr->TimeRcvd = now;
+            rr->UnansweredQueries = 0;
             SetNextCacheCheckTimeForRecord(m, rr);
         }
     }
             SetNextCacheCheckTimeForRecord(m, rr);
         }
     }
@@ -2281,7 +2548,7 @@ mDNSlocal void FinishDNSSECVerification(mDNS *const m, DNSSECVerifier *dv)
             // unlinks the resultKey and resultRRSig
             if (!AuthChainAdd(dv, resultKey, resultRRSig))
             {
             // unlinks the resultKey and resultRRSig
             if (!AuthChainAdd(dv, resultKey, resultRRSig))
             {
-                dv->DVCallback(m, dv, DNSSEC_Indeterminate);
+                dv->DVCallback(m, dv, DNSSEC_Bogus);
                 return;
             }
             // The callback will be called when NSEC verification is done.
                 return;
             }
             // The callback will be called when NSEC verification is done.
@@ -2302,7 +2569,7 @@ mDNSlocal void FinishDNSSECVerification(mDNS *const m, DNSSECVerifier *dv)
             // unlinks the resultKey and resultRRSig
             if (!AuthChainAdd(dv, resultKey, resultRRSig))
             {
             // unlinks the resultKey and resultRRSig
             if (!AuthChainAdd(dv, resultKey, resultRRSig))
             {
-                dv->DVCallback(m, dv, DNSSEC_Indeterminate);
+                dv->DVCallback(m, dv, DNSSEC_Bogus);
                 return;
             }
             FreeDNSSECVerifierRRSets(dv);
                 return;
             }
             FreeDNSSECVerifierRRSets(dv);
@@ -2318,21 +2585,22 @@ mDNSlocal void FinishDNSSECVerification(mDNS *const m, DNSSECVerifier *dv)
         else
         {
             LogDNSSEC("FinishDNSSECVerification: ValidateDS failed %##s (%s)", dv->rrset->name.c, DNSTypeName(dv->rrset->rrtype));
         else
         {
             LogDNSSEC("FinishDNSSECVerification: ValidateDS failed %##s (%s)", dv->rrset->name.c, DNSTypeName(dv->rrset->rrtype));
-            dv->DVCallback(m, dv, DNSSEC_Insecure);
+            dv->DVCallback(m, dv, DNSSEC_Bogus);
             return;
         }
     }
     else
     {
         LogDNSSEC("FinishDNSSECVerification: Could not validate the rrset %##s (%s)", dv->origName.c, DNSTypeName(dv->origType));
             return;
         }
     }
     else
     {
         LogDNSSEC("FinishDNSSECVerification: Could not validate the rrset %##s (%s)", dv->origName.c, DNSTypeName(dv->origType));
-        dv->DVCallback(m, dv, DNSSEC_Insecure);
+        dv->DVCallback(m, dv, DNSSEC_Bogus);
         return;
     }
 }
 
         return;
     }
 }
 
-mDNSexport void StartDNSSECVerification(mDNS *const m, DNSSECVerifier *dv)
+mDNSexport void StartDNSSECVerification(mDNS *const m, void *context)
 {
     mDNSBool done;
 {
     mDNSBool done;
+    DNSSECVerifier *dv = (DNSSECVerifier *)context;
 
     done = GetAllRRSetsForVerification(m, dv);
     if (done)
 
     done = GetAllRRSetsForVerification(m, dv);
     if (done)
@@ -2347,7 +2615,7 @@ mDNSexport void StartDNSSECVerification(mDNS *const m, DNSSECVerifier *dv)
     else debugdnssec("StartDNSSECVerification: all rdata sets not available for sig verification next %d", dv->next);
 }
 
     else debugdnssec("StartDNSSECVerification: all rdata sets not available for sig verification next %d", dv->next);
 }
 
-mDNSlocal char *DNSSECStatusName(DNSSECStatus status)
+mDNSexport char *DNSSECStatusName(DNSSECStatus status)
 {
     switch (status)
     {
 {
     switch (status)
     {
@@ -2361,7 +2629,7 @@ mDNSlocal char *DNSSECStatusName(DNSSECStatus status)
 
 // We could not use GenerateNegativeResponse as it assumes m->CurrentQuestion to be set. Even if
 // we change that, we needs to fix its callers and so on. It is much simpler to call the callback.
 
 // We could not use GenerateNegativeResponse as it assumes m->CurrentQuestion to be set. Even if
 // we change that, we needs to fix its callers and so on. It is much simpler to call the callback.
-mDNSlocal void DeliverDNSSECStatus(mDNS *const m, ResourceRecord *answer, DNSSECStatus status)
+mDNSlocal void DeliverDNSSECStatus(mDNS *const m, DNSSECVerifier *dv, ResourceRecord *answer, DNSSECStatus status)
 {
 
     // Can't use m->CurrentQuestion as it may already be in use
 {
 
     // Can't use m->CurrentQuestion as it may already be in use
@@ -2369,6 +2637,12 @@ mDNSlocal void DeliverDNSSECStatus(mDNS *const m, ResourceRecord *answer, DNSSEC
         LogMsg("DeliverDNSSECStatus: ERROR!! m->ValidationQuestion already set: %##s (%s)",
                m->ValidationQuestion->qname.c, DNSTypeName(m->ValidationQuestion->qtype));
 
         LogMsg("DeliverDNSSECStatus: ERROR!! m->ValidationQuestion already set: %##s (%s)",
                m->ValidationQuestion->qname.c, DNSTypeName(m->ValidationQuestion->qtype));
 
+    BumpDNSSECStats(m, kStatsActionSet, kStatsTypeStatus, status);
+    BumpDNSSECStats(m, kStatsActionSet, kStatsTypeExtraPackets, dv->NumPackets);
+    mDNS_Lock(m);
+    BumpDNSSECStats(m, kStatsActionSet, kStatsTypeLatency, m->timenow - dv->StartTime);
+    mDNS_Unlock(m);
+
     m->ValidationQuestion = m->Questions;
     while (m->ValidationQuestion && m->ValidationQuestion != m->NewQuestions)
     {
     m->ValidationQuestion = m->Questions;
     while (m->ValidationQuestion && m->ValidationQuestion != m->NewQuestions)
     {
@@ -2389,9 +2663,16 @@ mDNSlocal void DeliverDNSSECStatus(mDNS *const m, ResourceRecord *answer, DNSSEC
         {
             LogDNSSEC("DeliverDNSSECStatus: Generating dnssec status %s for %##s (%s)", DNSSECStatusName(status),
                 q->qname.c, DNSTypeName(q->qtype));
         {
             LogDNSSEC("DeliverDNSSECStatus: Generating dnssec status %s for %##s (%s)", DNSSECStatusName(status),
                 q->qname.c, DNSTypeName(q->qtype));
-            if (q->QuestionCallback) q->QuestionCallback(m, q, &largerec.r.resrec, QC_dnssec);
+            if (q->QuestionCallback)
+            {
+                if (q->DNSSECAuthInfo)
+                    FreeDNSSECAuthChainInfo((AuthChain *)q->DNSSECAuthInfo);
+                q->DNSSECAuthInfo = AuthChainCopy(dv->ac);
+                q->DAIFreeCallback = FreeAuthChain;
+                q->QuestionCallback(m, q, &largerec.r.resrec, QC_dnssec);
+            }
         }
         }
-        else
+        else if (FollowCNAME(q, answer, QC_add))
         {
             LogDNSSEC("DeliverDNSSECStatus: Following CNAME dnssec status %s for %##s (%s)", DNSSECStatusName(status),
                 q->qname.c, DNSTypeName(q->qtype));
         {
             LogDNSSEC("DeliverDNSSECStatus: Following CNAME dnssec status %s for %##s (%s)", DNSSECStatusName(status),
                 q->qname.c, DNSTypeName(q->qtype));
@@ -2406,18 +2687,118 @@ mDNSlocal void DeliverDNSSECStatus(mDNS *const m, ResourceRecord *answer, DNSSEC
     m->ValidationQuestion = mDNSNULL;
 }
 
     m->ValidationQuestion = mDNSNULL;
 }
 
-mDNSlocal void DNSSECPositiveValidationCB(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status)
+// There is no work to be done if we could not validate DNSSEC (as the actual response for
+// the query has already been delivered) except in the case of CNAMEs where we did not follow
+// CNAMEs until we finished the DNSSEC processing.
+mDNSlocal void DNSSECNoResponse(mDNS *const m, DNSSECVerifier *dv)
 {
 {
-    RRVerifier *rrset;
-    RRVerifier *rv;
     CacheGroup *cg;
     CacheRecord *cr;
     mDNSu32 slot, namehash;
     CacheGroup *cg;
     CacheRecord *cr;
     mDNSu32 slot, namehash;
+    ResourceRecord *answer = mDNSNULL;
+
+    LogDNSSEC("DNSSECNoResponse: called");
+
+    if (dv->ValidationRequired != DNSSEC_VALIDATION_SECURE_OPTIONAL)
+    {
+        LogMsg("DNSSECNoResponse: ERROR!! ValidationRequired incorrect %d", dv->ValidationRequired);
+        return;
+    }
+
+    BumpDNSSECStats(m, kStatsActionSet, kStatsTypeStatus, DNSSEC_NoResponse);
+
+    slot = HashSlot(&dv->origName);
+    namehash = DomainNameHashValue(&dv->origName);
+
+    cg = CacheGroupForName(m, (const mDNSu32)slot, namehash, &dv->origName);
+    if (!cg)
+    {
+        LogDNSSEC("DNSSECNoResponse: cg NULL for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType));
+        goto done;
+    }
+
+    InitializeQuestion(m, &dv->q, dv->InterfaceID, &dv->origName, dv->origType, mDNSNULL, mDNSNULL);
+
+    // We don't have to reset ValidatingResponse (unlike in DeliverDNSSECStatus) as there are no
+    // RRSIGs that can match the original question
+    for (cr = cg->members; cr; cr = cr->next)
+    {
+        if (SameNameRecordAnswersQuestion(&cr->resrec, &dv->q))
+        {
+            answer = &cr->resrec;
+            break;
+        }
+    }
+
+    // It is not an error for things to disappear underneath
+    if (!answer)
+    {
+        LogDNSSEC("DNSSECNoResponse: answer NULL for %##s, %s", dv->origName.c, DNSTypeName(dv->origType));
+        goto done;
+    }
+    if (answer->rrtype == kDNSType_RRSIG)
+    {
+        LogDNSSEC("DNSSECNoResponse: RRSIG present for %##s, %s", dv->origName.c, DNSTypeName(dv->origType));
+        goto done;
+    }
+
+    // Can't use m->CurrentQuestion as it may already be in use
+    if (m->ValidationQuestion)
+        LogMsg("DNSSECNoResponse: ERROR!! m->ValidationQuestion already set: %##s (%s)",
+               m->ValidationQuestion->qname.c, DNSTypeName(m->ValidationQuestion->qtype));
+
+    m->ValidationQuestion = m->Questions;
+    while (m->ValidationQuestion && m->ValidationQuestion != m->NewQuestions)
+    {
+        DNSQuestion *q = m->ValidationQuestion;
+
+        if (q->ValidatingResponse || !q->ValidationRequired ||
+           (q->ValidationState != DNSSECValInProgress) || !ResourceRecordAnswersQuestion(answer, q))
+        {
+            m->ValidationQuestion = q->next;
+            continue;
+        }
+
+        // If we could not validate e.g., zone was not signed or bad delegation etc., 
+        // disable validation. Ideally, for long outstanding questions, we should try again when
+        // we switch networks. But for now, keep it simple.
+        // 
+        // Note: If we followed a CNAME with no dnssec protection, it is even more important that
+        // we disable validation as we don't want to deliver a "secure" dnssec response later e.g.,
+        // it is possible that the CNAME is not secure but the address records are secure. In this
+        // case, we don't want to deliver the secure response later as we followed a CNAME that was
+        // not protected with DNSSEC.
+
+        q->ValidationRequired = 0;
+        q->ValidationState = DNSSECValNotRequired;
+        
+        if (FollowCNAME(q, answer, QC_add))
+        {
+            LogDNSSEC("DNSSECNoResponse: Following CNAME for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+
+            mDNS_Lock(m); 
+            AnswerQuestionByFollowingCNAME(m, q, answer);
+            mDNS_Unlock(m);
+        }
+
+        if (m->ValidationQuestion == q)    // If m->ValidationQuestion was not auto-advanced, do it ourselves now
+            m->ValidationQuestion = q->next;
+    }
+    m->ValidationQuestion = mDNSNULL;
+
+done:
+    FreeDNSSECVerifier(m, dv);
+}
+
+mDNSlocal void DNSSECPositiveValidationCB(mDNS *const m, DNSSECVerifier *dv, CacheGroup *cg, ResourceRecord *answer, DNSSECStatus status)
+{
+    RRVerifier *rrset;
+    RRVerifier *rv;
+    CacheRecord *cr;
     mDNSu16 rrtype, rrclass;
     CacheRecord *const lrr = &largerec.r;
     mDNSu16 rrtype, rrclass;
     CacheRecord *const lrr = &largerec.r;
-    ResourceRecord *answer = mDNSNULL;
 
 
-    LogDNSSEC("DNSSECPositiveValidationCB: called status %s", DNSSECStatusName(status));
+    LogDNSSEC("DNSSECPositiveValidationCB: called %s for %##s (%s)", DNSSECStatusName(status), dv->origName.c, DNSTypeName(dv->origType));
 
     //
     // 1. Check to see if the rrset that was validated is the same as in cache. If they are not same,
 
     //
     // 1. Check to see if the rrset that was validated is the same as in cache. If they are not same,
@@ -2431,31 +2812,13 @@ mDNSlocal void DNSSECPositiveValidationCB(mDNS *const m, DNSSECVerifier *dv, DNS
     //
     // 3. If we are answering with CNAME, it is time to follow the CNAME if the response is secure
 
     //
     // 3. If we are answering with CNAME, it is time to follow the CNAME if the response is secure
 
-    slot = HashSlot(&dv->origName);
-    namehash = DomainNameHashValue(&dv->origName);
-
-    cg = CacheGroupForName(m, (const mDNSu32)slot, namehash, &dv->origName);
-    if (!cg)
-    {
-        LogDNSSEC("DNSSECPositiveValidationCB: cg NULL for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType));
-        goto done;
-    }
-    if (!dv->ac)
+    if (!dv->ac || status == DNSSEC_Insecure)
     {
     {
-        // If we don't have the AuthChain, it means we could not validate the rrset. Locate the
-        // original question based on dv->origName, dv->origType.
-        InitializeQuestion(m, &dv->q, dv->InterfaceID, &dv->origName, dv->origType, mDNSNULL, mDNSNULL);
-        // Need to be reset ValidatingResponse as we are looking for the cache record that would answer
-        // the original question
-        dv->q.ValidatingResponse = mDNSfalse;
-        for (cr = cg->members; cr; cr = cr->next)
-        {
-            if (SameNameRecordAnswersQuestion(&cr->resrec, &dv->q))
-            {
-                answer = &cr->resrec;
-                break;
-            }
-        }
+        // For Insecure status, the auth chain contains information about the trust
+        // chain starting from the known trust anchor. The rrsets are not related to
+        // the origName like in Bogus or Secure.
+        if (!answer)
+            LogMsg("DNSSECPositiveValidationCB: ERROR: answer NULL");
     }
     else
     {
     }
     else
     {
@@ -2528,66 +2891,55 @@ mDNSlocal void DNSSECPositiveValidationCB(mDNS *const m, DNSSECVerifier *dv, DNS
     // It is not an error for things to disappear underneath
     if (!answer)
     {
     // It is not an error for things to disappear underneath
     if (!answer)
     {
-        LogDNSSEC("DNSSECPositiveValidationCB: answer NULL");
+        LogDNSSEC("DNSSECPositiveValidationCB: answer NULL for %##s, %s", dv->origName.c, DNSTypeName(dv->origType));
         goto done;
     }
 
         goto done;
     }
 
-    DeliverDNSSECStatus(m, answer, status);
+    DeliverDNSSECStatus(m, dv, answer, status);
     SetTTLRRSet(m, dv, status);
 
 done:
     FreeDNSSECVerifier(m, dv);
 }
 
     SetTTLRRSet(m, dv, status);
 
 done:
     FreeDNSSECVerifier(m, dv);
 }
 
-mDNSlocal void DNSSECNegativeValidationCB(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status)
+mDNSlocal void DNSSECNegativeValidationCB(mDNS *const m, DNSSECVerifier *dv, CacheGroup *cg, ResourceRecord *answer, DNSSECStatus status)
 {
     RRVerifier *rv;
 {
     RRVerifier *rv;
-    CacheGroup *cg;
     CacheRecord *cr;
     CacheRecord *cr;
-    mDNSu32 slot, namehash;
     mDNSu16 rrtype, rrclass;
     mDNSu16 rrtype, rrclass;
-    ResourceRecord *answer = mDNSNULL;
     AuthChain *ac;
 
     AuthChain *ac;
 
-    LogDNSSEC("DNSSECNegativeValidationCB: called %s", DNSSECStatusName(status));
+    LogDNSSEC("DNSSECNegativeValidationCB: called %s for %##s (%s)", DNSSECStatusName(status), dv->origName.c, DNSTypeName(dv->origType));
+
+    if (dv->parent)
+    {
+        // When NSEC/NSEC3s validation is completed, it calls the parent's DVCallback with the
+        // parent DNSSECVerifier which is the original one that started the verification. It itself
+        // should not have a parent. If the NSEC/NSEC3 validation results in another NSEC/NSEC3
+        // validation, it should chain up via the dv->parent all the way to the top.
+        LogMsg("DNSSECNegativeValidationCB: ERROR!! dv->parent is set for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType));
+        goto done;
+    }
 
 
-    // 1. Locate the negative cache record and check the cached NSEC records to see if it matches the
-    //    NSECs that were valiated. If the cached NSECS changed while the validation was in progress,
+    // 1. Locate the negative cache record and check the cached NSEC/NSEC3 records to see if it matches the
+    //    NSEC/NSEC3s that were valiated. If the cached NSEC/NSEC3s changed while the validation was in progress,
     //    we ignore the validation results.
     //
     // 2. Walk the question list to find the matching question. The original question that started
     //    the DNSSEC verification may or may not be there. As long as there is a matching question
     //    and waiting for the response, deliver the response.
     //
     //    we ignore the validation results.
     //
     // 2. Walk the question list to find the matching question. The original question that started
     //    the DNSSEC verification may or may not be there. As long as there is a matching question
     //    and waiting for the response, deliver the response.
     //
-    slot = HashSlot(&dv->origName);
-    namehash = DomainNameHashValue(&dv->origName);
-
-    cg = CacheGroupForName(m, (const mDNSu32)slot, namehash, &dv->origName);
-    if (!cg)
+    if (!dv->ac || status == DNSSEC_Insecure)
     {
     {
-        LogDNSSEC("DNSSECNegativeValidationCB: cg NULL for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType));
-        goto done;
+        // For Insecure status, the auth chain contains information about the trust
+        // chain starting from the known trust anchor. The rrsets are not related to
+        // the origName like in Bogus or Secure.
+        if (!answer)
+            LogMsg("DNSSECNegativeValidationCB: ERROR: answer NULL");
     }
     }
-    if (!dv->ac)
+    else
     {
     {
-        // If we don't have the AuthChain, it means we could not validate the rrset. Locate the
-        // original question based on dv->origName, dv->origType.
-        InitializeQuestion(m, &dv->q, dv->InterfaceID, &dv->origName, dv->origType, mDNSNULL, mDNSNULL);
-        // Need to be reset ValidatingResponse as we are looking for the cache record that would answer
-        // the original question
-        dv->q.ValidatingResponse = mDNSfalse;
-        for (cr = cg->members; cr; cr = cr->next)
-        {
-            if (SameNameRecordAnswersQuestion(&cr->resrec, &dv->q))
-            {
-                answer = &cr->resrec;
-                break;
-            }
-        }
-    }
-    else
-    {
-        if (!dv->ac->rrset)
+        if (!dv->ac->rrset)
         {
             LogMsg("DNSSECNegativeValidationCB: ERROR!! Validated RRSET NULL");
             goto done;
         {
             LogMsg("DNSSECNegativeValidationCB: ERROR!! Validated RRSET NULL");
             goto done;
@@ -2600,8 +2952,11 @@ mDNSlocal void DNSSECNegativeValidationCB(mDNS *const m, DNSSECVerifier *dv, DNS
         {
             for (rv = ac->rrset; rv; rv = rv->next)
             {
         {
             for (rv = ac->rrset; rv; rv = rv->next)
             {
-                if (rv->rrtype == kDNSType_NSEC)
+                if (rv->rrtype == kDNSType_NSEC || rv->rrtype == kDNSType_NSEC3)
+                {
+                    LogDNSSEC("DNSSECNegativeValidationCB: Record %p %##s (%s) marking zero", rv, rv->name.c, DNSTypeName(rv->rrtype));
                     rv->found = 0;
                     rv->found = 0;
+                }
             }
         }
 
             }
         }
 
@@ -2615,19 +2970,19 @@ mDNSlocal void DNSSECNegativeValidationCB(mDNS *const m, DNSSECVerifier *dv, DNS
                 for (ncr = cr->nsec; ncr; ncr = ncr->next)
                 {
                     // We have RRSIGs for the NSECs cached there too
                 for (ncr = cr->nsec; ncr; ncr = ncr->next)
                 {
                     // We have RRSIGs for the NSECs cached there too
-                    if (ncr->resrec.rrtype != kDNSType_NSEC)
+                    if (ncr->resrec.rrtype != kDNSType_NSEC && ncr->resrec.rrtype != kDNSType_NSEC3)
                         continue;
                     for (ac = dv->ac; ac; ac = ac->next)
                     {
                         for (rv = ac->rrset; rv; rv = rv->next)
                         {
                         continue;
                     for (ac = dv->ac; ac; ac = ac->next)
                     {
                         for (rv = ac->rrset; rv; rv = rv->next)
                         {
-                            if (rv->rrtype == kDNSType_NSEC && rv->rdlength == ncr->resrec.rdlength &&
+                            if ((rv->rrtype == kDNSType_NSEC || rv->rrtype == kDNSType_NSEC3) && rv->rdlength == ncr->resrec.rdlength &&
                                 rv->rdatahash == ncr->resrec.rdatahash)
                             {
                                 if (SameDomainName(ncr->resrec.name, &rv->name) &&
                                     SameRDataBody(&ncr->resrec, (const RDataBody *)rv->rdata, SameDomainName))
                                 {
                                 rv->rdatahash == ncr->resrec.rdatahash)
                             {
                                 if (SameDomainName(ncr->resrec.name, &rv->name) &&
                                     SameRDataBody(&ncr->resrec, (const RDataBody *)rv->rdata, SameDomainName))
                                 {
-                                    LogDNSSEC("DNSSECNegativeValidationCB: setting found %s", CRDisplayString(m, ncr));
+                                    LogDNSSEC("DNSSECNegativeValidationCB: Record %p %##s (%s) marking one", rv, rv->name.c, DNSTypeName(rv->rrtype));
                                     answer = &cr->resrec;
                                     rv->found = 1;
                                     break;
                                     answer = &cr->resrec;
                                     rv->found = 1;
                                     break;
@@ -2651,12 +3006,12 @@ mDNSlocal void DNSSECNegativeValidationCB(mDNS *const m, DNSSECVerifier *dv, DNS
         {
             for (rv = ac->rrset; rv; rv = rv->next)
             {
         {
             for (rv = ac->rrset; rv; rv = rv->next)
             {
-                if (rv->rrtype == kDNSType_NSEC)
+                if (rv->rrtype == kDNSType_NSEC || rv->rrtype == kDNSType_NSEC3)
                 {
                     if (!rv->found)
                     {
                         // We had more elements in the validated set, re-validate
                 {
                     if (!rv->found)
                     {
                         // We had more elements in the validated set, re-validate
-                        LogDNSSEC("DNSSECNegativeValidationCB: Record %##s (%s) not found in the cache", rv->name.c, DNSTypeName(rv->rrtype));
+                        LogDNSSEC("DNSSECNegativeValidationCB: Record %p %##s (%s) not found in the cache", rv, rv->name.c, DNSTypeName(rv->rrtype));
                         goto done;
                     }
                     rv->found = 0;
                         goto done;
                     }
                     rv->found = 0;
@@ -2668,26 +3023,83 @@ mDNSlocal void DNSSECNegativeValidationCB(mDNS *const m, DNSSECVerifier *dv, DNS
     // It is not an error for things to disappear underneath
     if (!answer)
     {
     // It is not an error for things to disappear underneath
     if (!answer)
     {
-        LogDNSSEC("DNSSECNegativeValidationCB: answer NULL");
+        LogDNSSEC("DNSSECNegativeValidationCB: answer NULL for %##s, %s", dv->origName.c, DNSTypeName(dv->origType));
         goto done;
     }
 
         goto done;
     }
 
-    DeliverDNSSECStatus(m, answer, status); 
+    DeliverDNSSECStatus(m, dv, answer, status); 
     SetTTLRRSet(m, dv, status);
 
 done:
     FreeDNSSECVerifier(m, dv);
 }
 
     SetTTLRRSet(m, dv, status);
 
 done:
     FreeDNSSECVerifier(m, dv);
 }
 
+mDNSlocal void DNSSECValidationCB(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status)
+{
+    mDNSu32 slot, namehash;
+    CacheGroup *cg;
+    CacheRecord *cr;
+
+    LogDNSSEC("DNSSECValidationCB: called %s for %##s (%s)", DNSSECStatusName(status), dv->origName.c, DNSTypeName(dv->origType));
+
+    // Currently, if we receive anything other than secure, we abort DNSSEC validation for
+    // the optional case.
+    if (dv->ValidationRequired == DNSSEC_VALIDATION_SECURE_OPTIONAL && status != DNSSEC_Secure)
+    {
+        DNSSECNoResponse(m, dv);
+        return; 
+    }
+
+    if (dv->ValidationRequired == DNSSEC_VALIDATION_SECURE && !dv->InsecureProofDone && status == DNSSEC_Bogus)
+    {
+        dv->InsecureProofDone = 1;
+        ProveInsecure(m, dv, mDNSNULL, mDNSNULL);
+        return;
+    }
+    slot = HashSlot(&dv->origName);
+    namehash = DomainNameHashValue(&dv->origName);
+
+    cg = CacheGroupForName(m, (const mDNSu32)slot, namehash, &dv->origName);
+    if (!cg)
+    {
+        LogDNSSEC("DNSSECValidationCB: cg NULL for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType));
+        FreeDNSSECVerifier(m, dv);
+        return;
+    }
+    InitializeQuestion(m, &dv->q, dv->InterfaceID, &dv->origName, dv->origType, mDNSNULL, mDNSNULL);
+    // Need to be reset ValidatingResponse as we are looking for the cache record that would answer
+    // the original question
+    dv->q.ValidatingResponse = mDNSfalse;
+    for (cr = cg->members; cr; cr = cr->next)
+    {
+        if (SameNameRecordAnswersQuestion(&cr->resrec, &dv->q))
+        {
+            if (cr->resrec.RecordType == kDNSRecordTypePacketNegative)
+                DNSSECNegativeValidationCB(m, dv, cg, &cr->resrec, status);
+            else
+                DNSSECPositiveValidationCB(m, dv, cg, &cr->resrec, status);
+            return;
+        }
+    }
+}
+
 mDNSexport void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion *q)
 {
     mDNSu32 slot = HashSlot(&q->qname);
     CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
     CacheRecord *rr;
 mDNSexport void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion *q)
 {
     mDNSu32 slot = HashSlot(&q->qname);
     CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
     CacheRecord *rr;
+    mDNSBool first = mDNSfalse;
+    static mDNSBool TrustAnchorsUpdated = mDNSfalse;
 
     LogDNSSEC("VerifySignature called for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
 
     LogDNSSEC("VerifySignature called for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+    if (!TrustAnchorsUpdated)
+    {
+        TrustAnchorsUpdated = mDNStrue;
+        UpdateTrustAnchors(m);
+    }
     if (!dv)
     {
     if (!dv)
     {
+        first = mDNStrue;
         if (!q->qDNSServer || q->qDNSServer->cellIntf)
         {
             LogDNSSEC("VerifySignature: Disabled");
         if (!q->qDNSServer || q->qDNSServer->cellIntf)
         {
             LogDNSSEC("VerifySignature: Disabled");
@@ -2695,8 +3107,12 @@ mDNSexport void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion *
         }
         // We assume that the verifier's question has been initialized here so that ValidateWithNSECS below
         // knows what it has prove the non-existence of.
         }
         // We assume that the verifier's question has been initialized here so that ValidateWithNSECS below
         // knows what it has prove the non-existence of.
-        dv = AllocateDNSSECVerifier(m, &q->qname, q->qtype, q->InterfaceID, DNSSECPositiveValidationCB, VerifySigCallback);
-        if (!dv) { LogMsg("VerifySignature: ERROR!! memory alloc failed"); return; }
+        dv = AllocateDNSSECVerifier(m, &q->qname, q->qtype, q->InterfaceID, q->ValidationRequired, DNSSECValidationCB, VerifySigCallback);
+        if (!dv)
+        {
+            LogMsg("VerifySignature: ERROR!! memory alloc failed");
+            return;
+        }
     }
 
     // If we find a CNAME response to the question, remember what qtype
     }
 
     // If we find a CNAME response to the question, remember what qtype
@@ -2732,13 +3148,12 @@ mDNSexport void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion *
             // questions. Later as part of validating the response, we might get a NSEC response.
             if (rr->resrec.RecordType == kDNSRecordTypePacketNegative && DNSSECQuestion(q))
             {
             // questions. Later as part of validating the response, we might get a NSEC response.
             if (rr->resrec.RecordType == kDNSRecordTypePacketNegative && DNSSECQuestion(q))
             {
-                dv->DVCallback = DNSSECNegativeValidationCB;
                 // If we can't find the NSEC, we can't validate. This can happens if we are
                 // behind a non-DNSSEC aware CPE/server.
                 if (!rr->nsec)
                 {
                     LogDNSSEC("VerifySignature: No nsecs found for %s", CRDisplayString(m, rr));
                 // If we can't find the NSEC, we can't validate. This can happens if we are
                 // behind a non-DNSSEC aware CPE/server.
                 if (!rr->nsec)
                 {
                     LogDNSSEC("VerifySignature: No nsecs found for %s", CRDisplayString(m, rr));
-                    dv->DVCallback(m, dv, DNSSEC_Insecure);
+                    dv->DVCallback(m, dv, DNSSEC_Bogus);
                     return;
                 }
                 ValidateWithNSECS(m, dv, rr);
                     return;
                 }
                 ValidateWithNSECS(m, dv, rr);
@@ -2747,21 +3162,29 @@ mDNSexport void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion *
 
             if (AddRRSetToVerifier(dv, &rr->resrec, mDNSNULL, RRVS_rr) != mStatus_NoError)
             {
 
             if (AddRRSetToVerifier(dv, &rr->resrec, mDNSNULL, RRVS_rr) != mStatus_NoError)
             {
-                dv->DVCallback(m, dv, DNSSEC_Indeterminate);
+                dv->DVCallback(m, dv, DNSSEC_Bogus);
                 return;
             }
         }
     if (!dv->rrset)
     {
         LogMsg("VerifySignature: rrset mDNSNULL for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType));
                 return;
             }
         }
     if (!dv->rrset)
     {
         LogMsg("VerifySignature: rrset mDNSNULL for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType));
-        dv->DVCallback(m, dv, DNSSEC_Indeterminate);
+        dv->DVCallback(m, dv, DNSSEC_Bogus);
         return;
     }
     dv->next = RRVS_rrsig;
         return;
     }
     dv->next = RRVS_rrsig;
-    StartDNSSECVerification(m, dv);
+    // Delay this so that the mDNS "core" can deliver all the results before
+    // we can deliver the dnssec result
+    if (first)
+    {
+        mDNSPlatformDispatchAsync(m, dv, StartDNSSECVerification);
+    }
+    else
+    {
+        StartDNSSECVerification(m, dv);
+    }
 }
 
 }
 
-
 mDNSlocal mDNSBool TrustedKeyPresent(mDNS *const m, DNSSECVerifier *dv)
 {
     rdataRRSig *rrsig;
 mDNSlocal mDNSBool TrustedKeyPresent(mDNS *const m, DNSSECVerifier *dv)
 {
     rdataRRSig *rrsig;
@@ -2879,7 +3302,7 @@ mDNSlocal mStatus TrustedKey(mDNS *const m, DNSSECVerifier *dv)
             digestLen = ta->digestLen;
 
             AlgAdd(dv->ctx, name.c, DomainNameLength(&name));
             digestLen = ta->digestLen;
 
             AlgAdd(dv->ctx, name.c, DomainNameLength(&name));
-            AlgAdd(dv->ctx, key, keyv->rdlength);
+            AlgAdd(dv->ctx, (const mDNSu8 *)key, keyv->rdlength);
 
             algRet = AlgVerify(dv->ctx, mDNSNULL, 0, digest, digestLen);
             AlgDestroy(dv->ctx);
 
             algRet = AlgVerify(dv->ctx, mDNSNULL, 0, digest, digestLen);
             AlgDestroy(dv->ctx);
@@ -2931,16 +3354,40 @@ mDNSlocal void VerifySigCallback(mDNS *const m, DNSQuestion *question, const Res
 
     debugdnssec("VerifySigCallback: AddRecord %d, dv %p", AddRecord, dv);
 
 
     debugdnssec("VerifySigCallback: AddRecord %d, dv %p", AddRecord, dv);
 
-    if (!AddRecord) return;
+    if (!AddRecord)
+        return;
 
 
-    LogDNSSEC("VerifySigCallback: Called with record %s", RRDisplayString(m, answer));
+    // After the first ADD event, we should ideally stop the question. If we don't stop
+    // the question, we might get more callbacks and that can cause problems. For example,
+    // in the first callback, we could start a insecure proof and while that is in progress,
+    // if we get more callbacks, we will try to start another insecure proof. As we already
+    // started an insecure proof, we won't start another but terminate the verification
+    // process where we free the current DNSSECVerifier while the first insecure proof is
+    // still referencing it.
+    //
+    // But there are cases below which might return if we have not received the right answer
+    // yet e.g., no RRSIGs. In that case if the question is stopped, we will never get any
+    // callbacks again and also we leak "dv". Hence it is important that we either process
+    // the result or wait for more results. Note that the question eventually times out
+    // and cleans up the "dv" i.e., we don't wait forever.
+    
+    if (!answer)
+    {
+        LogDNSSEC("VerifySigCallback: Question %##s (%s) no dnssec response", question->qname.c, DNSTypeName(question->qtype));
+        mDNS_StopQuery(m, question);
+        dv->DVCallback(m, dv, DNSSEC_Bogus);
+        return;
+    }
 
 
+    LogDNSSEC("VerifySigCallback(%p): Called with record %s for question %##s (%s)", dv, RRDisplayString(m, answer), question->qname.c,
+        DNSTypeName(question->qtype));
     mDNS_Lock(m);
     if ((m->timenow - question->StopTime) >= 0)
     {
         mDNS_Unlock(m);
         LogDNSSEC("VerifySigCallback: Question %##s (%s) timed out", question->qname.c, DNSTypeName(question->qtype));
     mDNS_Lock(m);
     if ((m->timenow - question->StopTime) >= 0)
     {
         mDNS_Unlock(m);
         LogDNSSEC("VerifySigCallback: Question %##s (%s) timed out", question->qname.c, DNSTypeName(question->qtype));
-        dv->DVCallback(m, dv, DNSSEC_Indeterminate);
+        mDNS_StopQuery(m, question);
+        dv->DVCallback(m, dv, DNSSEC_Bogus);
         return;
     }
     mDNS_Unlock(m);
         return;
     }
     mDNS_Unlock(m);
@@ -2950,14 +3397,15 @@ mDNSlocal void VerifySigCallback(mDNS *const m, DNSQuestion *question, const Res
         CacheRecord *cr;
         LogDNSSEC("VerifySigCallback: Received a negative answer with record %s, AddRecord %d",
                   RRDisplayString(m, answer), AddRecord);
         CacheRecord *cr;
         LogDNSSEC("VerifySigCallback: Received a negative answer with record %s, AddRecord %d",
                   RRDisplayString(m, answer), AddRecord);
+        mDNS_StopQuery(m, question);
         cr = NegativeCacheRecordForRR(m, answer);
         if (cr && cr->nsec)
         {
         cr = NegativeCacheRecordForRR(m, answer);
         if (cr && cr->nsec)
         {
-            dv->DVCallback = DNSSECNegativeValidationCB;
             ValidateWithNSECS(m, dv, cr);
         }
         else
         {
             ValidateWithNSECS(m, dv, cr);
         }
         else
         {
+            
             LogDNSSEC("VerifySigCallback: Missing record (%s) Negative Cache Record %p", RRDisplayString(m, answer), cr);
             dv->DVCallback(m, dv, DNSSEC_Bogus);
         }
             LogDNSSEC("VerifySigCallback: Missing record (%s) Negative Cache Record %p", RRDisplayString(m, answer), cr);
             dv->DVCallback(m, dv, DNSSEC_Bogus);
         }
@@ -2967,7 +3415,8 @@ mDNSlocal void VerifySigCallback(mDNS *const m, DNSQuestion *question, const Res
     if (!dv->rrset)
     {
         LogMsg("VerifySigCallback: ERROR!! rrset NULL");
     if (!dv->rrset)
     {
         LogMsg("VerifySigCallback: ERROR!! rrset NULL");
-        dv->DVCallback(m, dv, DNSSEC_Indeterminate);
+        mDNS_StopQuery(m, question);
+        dv->DVCallback(m, dv, DNSSEC_Bogus);
         return;
     }
 
         return;
     }
 
@@ -3018,6 +3467,7 @@ mDNSlocal void VerifySigCallback(mDNS *const m, DNSQuestion *question, const Res
             LogDNSSEC("VerifySigCallback: RRVS_rrsig called with %s", RRDisplayString(m, answer));
             return;
         }
             LogDNSSEC("VerifySigCallback: RRVS_rrsig called with %s", RRDisplayString(m, answer));
             return;
         }
+        mDNS_StopQuery(m, question);
         if (CheckRRSIGForRRSet(m, dv, &negcr) != mStatus_NoError)
         {
             LogDNSSEC("VerifySigCallback: Unable to find RRSIG for %##s (%s), question %##s", dv->rrset->name.c,
         if (CheckRRSIGForRRSet(m, dv, &negcr) != mStatus_NoError)
         {
             LogDNSSEC("VerifySigCallback: Unable to find RRSIG for %##s (%s), question %##s", dv->rrset->name.c,
@@ -3042,11 +3492,12 @@ mDNSlocal void VerifySigCallback(mDNS *const m, DNSQuestion *question, const Res
                 question->qtype);
             return;
         }
                 question->qtype);
             return;
         }
+        mDNS_StopQuery(m, question);
         if (CheckKeyForRRSIG(m, dv, &negcr) != mStatus_NoError)
         {
             LogDNSSEC("VerifySigCallback: Unable to find DNSKEY for %##s (%s), question %##s", dv->rrset->name.c,
                       DNSTypeName(dv->rrset->rrtype), question->qname.c);
         if (CheckKeyForRRSIG(m, dv, &negcr) != mStatus_NoError)
         {
             LogDNSSEC("VerifySigCallback: Unable to find DNSKEY for %##s (%s), question %##s", dv->rrset->name.c,
                       DNSTypeName(dv->rrset->rrtype), question->qname.c);
-            dv->DVCallback(m, dv, DNSSEC_Indeterminate);
+            dv->DVCallback(m, dv, DNSSEC_Bogus);
             return;
         }
         break;
             return;
         }
         break;
@@ -3068,6 +3519,7 @@ mDNSlocal void VerifySigCallback(mDNS *const m, DNSQuestion *question, const Res
                 question->qtype);
             return;
         }
                 question->qtype);
             return;
         }
+        mDNS_StopQuery(m, question);
         if (CheckRRSIGForKey(m, dv, &negcr) != mStatus_NoError)
         {
             LogDNSSEC("VerifySigCallback: Unable to find RRSIG for %##s (%s), question %##s", dv->rrset->name.c,
         if (CheckRRSIGForKey(m, dv, &negcr) != mStatus_NoError)
         {
             LogDNSSEC("VerifySigCallback: Unable to find RRSIG for %##s (%s), question %##s", dv->rrset->name.c,
@@ -3085,6 +3537,7 @@ mDNSlocal void VerifySigCallback(mDNS *const m, DNSQuestion *question, const Res
         {
             LogDNSSEC("VerifySigCallback: RRVS_ds rrset type %s, %##s received before DS", DNSTypeName(rrtype), question->qname.c);
         }
         {
             LogDNSSEC("VerifySigCallback: RRVS_ds rrset type %s, %##s received before DS", DNSTypeName(rrtype), question->qname.c);
         }
+        mDNS_StopQuery(m, question);
         // It is not an error if we don't find the DS record as we could have
         // a trusted key. Or this is not a secure delegation which will be handled
         // below.
         // It is not an error if we don't find the DS record as we could have
         // a trusted key. Or this is not a secure delegation which will be handled
         // below.
@@ -3105,13 +3558,13 @@ mDNSlocal void VerifySigCallback(mDNS *const m, DNSQuestion *question, const Res
                 dv->DVCallback(m, dv, DNSSEC_Bogus);
                 return;
             }
                 dv->DVCallback(m, dv, DNSSEC_Bogus);
                 return;
             }
-            dv->DVCallback = DNSSECNegativeValidationCB;
             ValidateWithNSECS(m, dv, negcr);
             return;
         }
         break;
     default:
         LogDNSSEC("VerifySigCallback: ERROR!! default case rrset %##s question %##s", dv->rrset->name.c, question->qname.c);
             ValidateWithNSECS(m, dv, negcr);
             return;
         }
         break;
     default:
         LogDNSSEC("VerifySigCallback: ERROR!! default case rrset %##s question %##s", dv->rrset->name.c, question->qname.c);
+        mDNS_StopQuery(m, question);
         dv->DVCallback(m, dv, DNSSEC_Bogus);
         return;
     }
         dv->DVCallback(m, dv, DNSSEC_Bogus);
         return;
     }
@@ -3133,3 +3586,526 @@ mDNSlocal void VerifySigCallback(mDNS *const m, DNSQuestion *question, const Res
     }
     FinishDNSSECVerification(m, dv);
 }
     }
     FinishDNSSECVerification(m, dv);
 }
+
+mDNSlocal TrustAnchor *FindTrustAnchor(mDNS *const m, const domainname *const name)
+{
+    TrustAnchor *ta;
+    TrustAnchor *matchTA = mDNSNULL;
+    TrustAnchor *rootTA = mDNSNULL;
+    int currmatch = 0;
+    int match;
+    mDNSu32 currTime = mDNSPlatformUTC();
+
+    for (ta = m->TrustAnchors; ta; ta = ta->next)
+    {
+        if (DNS_SERIAL_LT(ta->validUntil, currTime))
+        {
+            LogDNSSEC("FindTrustAnchor: Expired: currentTime %d, ExpireTime %d", (int)currTime, ta->validUntil);
+            continue;
+        }
+        if (DNS_SERIAL_LT(currTime, ta->validFrom))
+        {
+            LogDNSSEC("FindTrustAnchor: Future: currentTime %d, InceptTime %d", (int)currTime, ta->validFrom);
+            continue;
+        }
+
+        if (SameDomainName((const domainname *)"\000", &ta->zone))
+            rootTA = ta;
+
+        match = CountLabelsMatch(&ta->zone, name);
+        if (match > currmatch)
+        {
+            currmatch = match;
+            matchTA = ta;
+        }
+    }
+    if (matchTA)
+    {
+        LogDNSSEC("FindTrustAnhcor: matched %##s", matchTA->zone.c);
+        return matchTA;
+    }
+    else if (rootTA)
+    {
+        LogDNSSEC("FindTrustAnhcor: matched rootTA %##s", rootTA->zone.c);
+        return rootTA;
+    }
+    else
+    {
+        LogDNSSEC("FindTrustAnhcor: No Trust Anchor");
+        return mDNSNULL;
+    }
+}
+
+mDNSlocal void DeliverInsecureProofResultAsync(mDNS *const m, void *context)
+{
+    InsecureContext *ic = (InsecureContext *)context;
+    ic->dv->DVCallback(m, ic->dv, ic->status);
+    if (ic->q.ThisQInterval != -1)
+    {
+        LogMsg("DeliverInsecureProofResultAsync: ERROR!! Question %##s (%s) not stopped already", ic->q.qname.c, DNSTypeName(ic->q.qtype));
+        mDNS_StopQuery(m, &ic->q);
+    }
+    mDNSPlatformMemFree(ic);
+}
+
+mDNSlocal void DeliverInsecureProofResult(mDNS *const m, InsecureContext *ic, DNSSECStatus status)
+{
+    // If the status is Bogus, restore the original auth chain before the insecure
+    // proof.
+    if (status == DNSSEC_Bogus)
+    {
+        LogDNSSEC("DeliverInsecureProofResult: Restoring the auth chain");
+        if (ic->dv->ac)
+        {
+            FreeDNSSECAuthChainInfo(ic->dv->ac);
+        }
+        ResetAuthChain(ic->dv);
+        ic->dv->ac = ic->dv->saveac;
+        if (ic->dv->ac)
+        {
+            AuthChain *tmp = ic->dv->ac;
+            AuthChain **tail = &tmp->next;
+            while (tmp->next)
+            {
+                tail = &tmp->next;
+                tmp = tmp->next;
+            }
+            ic->dv->actail = tail;
+        }
+        ic->dv->saveac = mDNSNULL;
+    }
+    else if (ic->dv->saveac)
+    {
+        FreeDNSSECAuthChainInfo(ic->dv->saveac);
+        ic->dv->saveac = mDNSNULL;
+    }
+    ic->status = status;
+    // Stop the question before we schedule the block so that we don't receive additional
+    // callbacks again. Once the block runs, it will free the "ic" and you can't
+    // have another block queued up. This can happen if we receive a callback after we
+    // queue the block below.
+    if (ic->q.ThisQInterval != -1)
+        mDNS_StopQuery(m, &ic->q);
+    mDNSPlatformDispatchAsync(m, ic, DeliverInsecureProofResultAsync);
+}
+
+mDNSlocal mDNSBool AlgorithmSupported(rdataDS *ds)
+{
+    switch(ds->digestType)
+    {
+    case SHA1_DIGEST_TYPE:
+    case SHA256_DIGEST_TYPE:
+        break;
+    default:
+        LogDNSSEC("AlgorithmSupported: Unsupported digest %d", ds->digestType);
+        return mDNSfalse;
+    }
+
+    switch(ds->alg)
+    {
+    case CRYPTO_RSA_NSEC3_SHA1:
+    case CRYPTO_RSA_SHA1:
+    case CRYPTO_RSA_SHA256:
+    case CRYPTO_RSA_SHA512:
+        return mDNStrue;
+    default:
+        LogDNSSEC("AlgorithmSupported: Unsupported algorithm %d", ds->alg);
+        return mDNSfalse;
+    }
+}
+
+// Note: This function is called when DNSSEC results are delivered (from DeliverDNSSECStatus) and we can't deliver DNSSEC result
+// again within this function as "m->ValidationQuestion" is already in use. Hence we should dispatch off the delivery of insecure
+// results asynchronously.
+//
+// Insecure proof callback can deliver either insecure or bogus, but never secure result.
+mDNSlocal void ProveInsecureCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+{
+    InsecureContext *ic = (InsecureContext *)question->QuestionContext;
+    DNSSECVerifier *pdv = ic->dv;
+    AuthChain *ac;
+
+    (void) answer;
+
+    if (!AddRecord)
+        return;
+
+    mDNS_Lock(m);
+    if ((m->timenow - question->StopTime) >= 0)
+    {
+        mDNS_Unlock(m);
+        LogDNSSEC("ProveInsecureCallback: Question %##s (%s) timed out", question->qname.c, DNSTypeName(question->qtype));
+        DeliverInsecureProofResult(m, ic, DNSSEC_Bogus);
+        return;
+    }
+    mDNS_Unlock(m);
+
+    // We only need to handle the actual DNSSEC results and the ones that are secure. Anything else results in
+    // bogus.
+    if (AddRecord != QC_dnssec)
+    {
+        LogDNSSEC("ProveInsecureCallback: Question %##s (%s), AddRecord %d, answer %s", question->qname.c,
+            DNSTypeName(question->qtype), AddRecord, RRDisplayString(m, answer));
+        return;
+    }
+
+    LogDNSSEC("ProveInsecureCallback: ic %p Question %##s (%s), DNSSEC status %s", ic, question->qname.c, DNSTypeName(question->qtype),
+            DNSSECStatusName(question->ValidationStatus));
+
+    // Insecure is delivered for NSEC3 OptOut 
+    if (question->ValidationStatus != DNSSEC_Secure && question->ValidationStatus != DNSSEC_Insecure)
+    {
+        LogDNSSEC("ProveInsecureCallback: Question %##s (%s) returned DNSSEC status %s", question->qname.c,
+            DNSTypeName(question->qtype), DNSSECStatusName(question->ValidationStatus));
+        goto done;
+    }
+    ac = (AuthChain *)question->DNSSECAuthInfo;
+    if (!ac)
+    {
+        LogDNSSEC("ProveInsecureCallback: ac NULL for question %##s, %s", question->qname.c, DNSTypeName(question->qtype));
+        goto done;
+    }
+    if (!ac->rrset)
+    {
+        LogDNSSEC("ProveInsecureCallback: ac->rrset NULL for question %##s, %s", question->qname.c, DNSTypeName(question->qtype));
+        goto done;
+    }
+    if (ac->rrset->rrtype != kDNSType_DS && ac->rrset->rrtype != kDNSType_NSEC && ac->rrset->rrtype != kDNSType_NSEC3)
+    {
+        LogDNSSEC("ProveInsecureCallback: ac->rrset->rrtype %##s (%s) not handled", ac->rrset->name.c,
+            DNSTypeName(ac->rrset->rrtype));
+        goto done;
+    }
+    AuthChainLink(pdv, ac);
+    question->DNSSECAuthInfo = mDNSNULL;
+    if (ac->rrset->rrtype == kDNSType_DS)
+    {
+        rdataDS *ds = (rdataDS *)ac->rrset->rdata;
+
+        // If the delegation is secure, but the underlying zone is signed with an unsupported
+        // algorithm, then we can't verify it. Deliver insecure in that case.
+        if (!AlgorithmSupported(ds))
+        {
+            LogDNSSEC("ProveInsecureCallback: Unsupported algorithm %d or digest %d", ds->alg, ds->digestType);
+            DeliverInsecureProofResult(m, ic, DNSSEC_Insecure);
+            return;
+        }
+
+        // If the delegation is secure and the name that we queried for is same as the original
+        // name that started the insecure proof, then something is not right. We started the
+        // insecure proof e.g., the zone is not signed, but we are able to validate a DS for
+        // the same name which implies that the zone is signed (whose algorithm we support) and
+        // we should not have started the insecurity proof in the first place.
+        if (SameDomainName(&question->qname, &pdv->origName))
+        {
+            LogDNSSEC("ProveInsecureCallback: Insecure proof reached original name %##s, error", question->qname.c);
+            DeliverInsecureProofResult(m, ic, DNSSEC_Bogus);
+            return;
+        }
+
+        LogDNSSEC("ProveInsecureCallback: Trying one more level down");
+        ProveInsecure(m, pdv, ic, mDNSNULL);
+    }
+    else if (ac->rrset->rrtype == kDNSType_NSEC || ac->rrset->rrtype == kDNSType_NSEC3)
+    {
+        CacheRecord *cr;
+
+        if (ac->rrset->rrtype == kDNSType_NSEC)
+            cr = NSECRecordIsDelegation(m, &question->qname, question->qtype);
+        else
+            cr = NSEC3RecordIsDelegation(m, &question->qname, question->qtype);
+               if (cr)
+        {
+            LogDNSSEC("ProveInsecureCallback: Non-existence proved and %s is a delegation for %##s (%s)", CRDisplayString(m, cr),
+                               question->qname.c, DNSTypeName(question->qtype));
+            DeliverInsecureProofResult(m, ic, DNSSEC_Insecure);
+                       return;
+        }
+        // Could be a ENT. Go one more level down to see whether it is a secure delegation or not.
+        if (!SameDomainName(&question->qname, &pdv->origName))
+        {
+            LogDNSSEC("ProveInsecureCallback: Not a delegation %##s (%s), go one more level down", question->qname.c, DNSTypeName(question->qtype));
+            ProveInsecure(m, pdv, ic, mDNSNULL);
+        }
+        else
+        {
+             // Secure denial of existence and the name matches the original query. This means we should have
+             // received an NSEC (if the type does not exist) or signed records (if the name and type exists)
+             // and verified it successfully instead of starting the insecure proof. This could happen e.g.,
+             // Wildcard expanded answer received without NSEC/NSEC3s etc. Also, is it possible that the
+             // zone went from unsigned to signed in a short time ? For now, we return bogus.
+             LogDNSSEC("ProveInsecureCallback: Not a delegation %##s (%s), but reached original name", question->qname.c,
+                 DNSTypeName(question->qtype));
+             DeliverInsecureProofResult(m, ic, DNSSEC_Bogus);
+        }
+    }
+    return;
+done:
+    DeliverInsecureProofResult(m, ic, DNSSEC_Bogus);
+}
+
+// We return Insecure if we don't have a trust anchor or we have a trust anchor and
+// can prove that the delegation is not secure (and hence can't establish the trust
+// chain) or the delegation is possibly secure but we don't have the algorithm support
+// to prove that.
+mDNSexport void ProveInsecure(mDNS *const m, DNSSECVerifier *dv, InsecureContext *ic, domainname *trigger)
+{
+    TrustAnchor *ta;
+    domainname *sname;
+
+    if (ic == mDNSNULL)
+    {
+        ic = (InsecureContext *)mDNSPlatformMemAllocate(sizeof(InsecureContext));
+        if (!ic)
+        {
+            LogMsg("mDNSPlatformMemAllocate: ERROR!! memory alloc failed for ic");
+            return;
+        }
+        
+        // Save the AuthInfo while we are proving insecure. We don't want to mix up
+        // the auth chain for Bogus and Insecure. If we prove it to be insecure, we
+        // will add the chain corresponding to the insecure proof. Otherwise, we will
+        // restore this chain.
+        if (dv->ac)
+        {
+            if (!dv->saveac)
+            {
+                LogDNSSEC("ProveInsecure: saving authinfo");
+            }
+            else
+            {
+                LogDNSSEC("ProveInsecure: ERROR!! authinfo already set");
+                FreeDNSSECAuthChainInfo(dv->saveac);
+            }
+            dv->saveac = dv->ac;
+            ResetAuthChain(dv);
+        }
+        ic->dv = dv;
+        ic->q.ThisQInterval = -1;
+
+        if (trigger)
+        {
+            LogDNSSEC("ProveInsecure: Setting Trigger %##s", trigger->c);
+            ic->triggerLabelCount = CountLabels(trigger);
+        }
+        else
+        {
+            LogDNSSEC("ProveInsecure: No Trigger");
+            ic->triggerLabelCount = CountLabels(&dv->origName);
+        }
+
+        ta = FindTrustAnchor(m, &dv->origName);
+        if (!ta)
+        {
+            LogDNSSEC("ProveInsecure: TrustAnchor NULL");
+            DeliverInsecureProofResult(m, ic, DNSSEC_Insecure);
+            return;
+        }
+        // We want to skip the labels that is already matched by the trust anchor so
+        // that the first query starts just below the trust anchor
+        ic->skip = CountLabels(&dv->origName) - CountLabels(&ta->zone);
+        if (!ic->skip)
+        {
+            LogDNSSEC("ProveInsecure: origName %##s, skip is zero", dv->origName.c);
+            DeliverInsecureProofResult(m, ic, DNSSEC_Bogus);
+            return;
+        }
+    }
+    // Look for the DS record starting just below the trust anchor.
+    //
+    // 1. If we find an NSEC record, then see if it is a delegation. If it is, then
+    //    we are done. Otherwise, go down one more level.
+    //
+    // 2. If we find a DS record and no algorithm support, return "insecure". Otherwise, go
+    //    down one more level.
+    //
+    sname = (domainname *)SkipLeadingLabels(&dv->origName, (ic->skip ? ic->skip - 1 : 0));
+    if (!sname)
+    {
+        LogDNSSEC("ProveInsecure: sname NULL, origName %##s, skip %d", dv->origName.c, ic->skip);
+        DeliverInsecureProofResult(m, ic, DNSSEC_Bogus);
+        return;
+    }
+
+    // Insecurity proof is started during the normal bottom-up validation when we have a break in the trust
+    // chain e.g., we get NSEC/NSEC3s when looking up a DS record. Insecurity proof is top-down looking
+    // for a break in the trust chain. If we have already tried the validation (before the insecurity
+    // proof started) for this "sname", then don't bother with the proof. This happens sometimes, when 
+    // we can't prove whether a zone is insecurely delegated or not. For example, if we are looking up
+    // host1.secure-nods.secure.example and when we encounter secure-nods, there is no DS record in the
+    // parent. We start the insecurity proof remembering that "secure-nods.secure.example" is the trigger
+    // point. As part of the proof we reach "secure-nods.secure.example". Even though secure.example
+    // prove that the name "secure-nods.secure.example/DS" does not exist,  it can't prove that it is a
+    // delegation. So, we continue one more level down to host1.secure-nods.secure.example and we
+    // realize that we already tried the validation and hence abort here.
+
+    if (CountLabels(sname) > ic->triggerLabelCount)
+    {
+        LogDNSSEC("ProveInsecure: Beyond the trigger current name %##s, origName %##s", sname->c, dv->origName.c);
+        DeliverInsecureProofResult(m, ic, DNSSEC_Bogus);
+        return;
+    }
+
+    LogDNSSEC("ProveInsecure: OrigName %##s (%s), Current %##s", dv->origName.c, DNSTypeName(dv->origType), sname->c);
+    ic->skip--;
+    InitializeQuestion(m, &ic->q, dv->InterfaceID, sname, kDNSType_DS, ProveInsecureCallback, ic);
+    ic->q.ValidationRequired = DNSSEC_VALIDATION_INSECURE;
+    ic->q.ValidatingResponse = 0;
+    ic->q.DNSSECAuthInfo = mDNSNULL;
+    mDNS_StartQuery(m, &ic->q);
+}
+
+mDNSexport void BumpDNSSECStats(mDNS *const m, DNSSECStatsAction action, DNSSECStatsType type, mDNSu32 value)
+{
+    switch (type)
+    {
+    case kStatsTypeMemoryUsage:
+        if (action == kStatsActionIncrement)
+        {
+            m->DNSSECStats.TotalMemUsed += value;
+        }
+        else if (action == kStatsActionDecrement)
+        {
+            m->DNSSECStats.TotalMemUsed -= value;
+        }
+        break;
+    case kStatsTypeLatency:
+        if (action == kStatsActionSet)
+        {
+            if (value <= 4)
+            {
+                m->DNSSECStats.Latency0++;
+            }
+            else if (value <= 9)
+            {
+                m->DNSSECStats.Latency5++;
+            }
+            else if (value <= 19)
+            {
+                m->DNSSECStats.Latency10++;
+            }
+            else if (value <= 49)
+            {
+                m->DNSSECStats.Latency20++;
+            }
+            else if (value <= 99)
+            {
+                m->DNSSECStats.Latency50++;
+            }
+            else
+            {
+                m->DNSSECStats.Latency100++;
+            }
+        }
+        break;
+    case kStatsTypeExtraPackets:
+        if (action == kStatsActionSet)
+        {
+            if (value <= 2)
+            {
+                m->DNSSECStats.ExtraPackets0++;
+            }
+            else if (value <= 6)
+            {
+                m->DNSSECStats.ExtraPackets3++;
+            }
+            else if (value <= 9)
+            {
+                m->DNSSECStats.ExtraPackets7++;
+            }
+            else
+            {
+                m->DNSSECStats.ExtraPackets10++;
+            }
+        }
+        break;
+    case kStatsTypeStatus:
+        if (action == kStatsActionSet)
+        {
+            switch(value)
+            {
+            case DNSSEC_Secure:
+                m->DNSSECStats.SecureStatus++;
+                break;
+            case DNSSEC_Insecure:
+                m->DNSSECStats.InsecureStatus++;
+                break;
+            case DNSSEC_Indeterminate:
+                m->DNSSECStats.IndeterminateStatus++;
+                break;
+            case DNSSEC_Bogus:
+                m->DNSSECStats.BogusStatus++;
+                break;
+            case DNSSEC_NoResponse:
+                m->DNSSECStats.NoResponseStatus++;
+                break;
+            default:
+                LogMsg("BumpDNSSECStats: unknown status %d", value);
+            }
+        }
+        break;
+    case kStatsTypeMsgSize:
+        if (action == kStatsActionSet)
+        {
+            if (value <= 1024)
+            {
+                m->DNSSECStats.MsgSize0++;
+            }
+            else if (value <= 2048)
+            {
+                m->DNSSECStats.MsgSize1++;
+            }
+            else
+            {
+                m->DNSSECStats.MsgSize2++;
+            }
+        }
+        break;
+    case kStatsTypeProbe:
+        if (action == kStatsActionIncrement)
+        {
+            m->DNSSECStats.NumProbesSent += value;
+        }
+        break;
+    default:
+        LogMsg("BumpDNSSECStats: unknown type %d", type);
+    }
+    return;
+}
+
+#else // !DNSSEC_DISABLED
+
+mDNSexport void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion *q)
+{
+    (void)m;
+    (void)dv;
+    (void)q;
+}
+
+mDNSexport void BumpDNSSECStats(mDNS *const m, DNSSECStatsAction action, DNSSECStatsType type, mDNSu32 value)
+{
+    (void)m;
+    (void)action;
+    (void)type;
+    (void)value;
+}
+
+mDNSexport void InitializeQuestion(mDNS *const m, DNSQuestion *question, mDNSInterfaceID InterfaceID, const domainname *qname, mDNSu16 qtype, mDNSQuestionCallback *callback, void *context)
+{
+    (void) m;
+    (void) question;
+    (void) InterfaceID;
+    (void) qname;
+    (void) qtype;
+    (void) callback;
+    (void) context;
+}
+
+mDNSexport char *DNSSECStatusName(DNSSECStatus status)
+{
+    (void) status;
+    
+    return mDNSNULL;
+}
+
+#endif // !DNSSEC_DISABLED
index 91aabebea863aca75056757b8a916a694bcf7952..1a8d95351b82c62624f022024157845d96c28bae 100644 (file)
@@ -28,6 +28,7 @@ typedef enum
 typedef struct RRVerifier_struct RRVerifier;
 typedef struct DNSSECVerifier_struct DNSSECVerifier;
 typedef struct AuthChain_struct AuthChain;
 typedef struct RRVerifier_struct RRVerifier;
 typedef struct DNSSECVerifier_struct DNSSECVerifier;
 typedef struct AuthChain_struct AuthChain;
+typedef struct InsecureContext_struct InsecureContext;
 
 struct RRVerifier_struct
 {
 
 struct RRVerifier_struct
 {
@@ -53,6 +54,11 @@ struct AuthChain_struct
        RRVerifier *key;                // Public key for that RRSET
 };
 
        RRVerifier *key;                // Public key for that RRSET
 };
 
+#define ResetAuthChain(dv) {    \
+    (dv)->ac = mDNSNULL;        \
+    (dv)->actail = &((dv)->ac); \
+}
+
 typedef void DNSSECVerifierCallback (mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status);
 //
 // When we do a validation for a question, there might be additional validations that needs to be done e.g.,
 typedef void DNSSECVerifierCallback (mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status);
 //
 // When we do a validation for a question, there might be additional validations that needs to be done e.g.,
@@ -80,46 +86,72 @@ typedef void DNSSECVerifierCallback (mDNS *const m, DNSSECVerifier *dv, DNSSECSt
 #define WILDCARD_PROVES_NONAME_EXISTS   0x00000002
 #define NSEC_PROVES_NOTYPE_EXISTS       0x00000004
 #define NSEC_PROVES_NONAME_EXISTS       0x00000008
 #define WILDCARD_PROVES_NONAME_EXISTS   0x00000002
 #define NSEC_PROVES_NOTYPE_EXISTS       0x00000004
 #define NSEC_PROVES_NONAME_EXISTS       0x00000008
+#define NSEC3_OPT_OUT                                  0x00000010 // OptOut was set in NSEC3
 
 struct DNSSECVerifier_struct
 {
 
 struct DNSSECVerifier_struct
 {
-    domainname origName;    // Original question name that needs verification
-    mDNSu16 origType;       // Original question type corresponding to origName
-    mDNSu16 currQtype;      // Current question type that is being verified
-    mDNSInterfaceID InterfaceID; // InterfaceID of the question
+    domainname origName;            // Original question name that needs verification
+    mDNSu16 origType;               // Original question type corresponding to origName
+    mDNSu16 currQtype;              // Current question type that is being verified
+    mDNSInterfaceID InterfaceID;    // InterfaceID of the question
     DNSQuestion q;
     DNSQuestion q;
-    mDNSu8 recursed;           // Number of times recursed during validation
+    mDNSu8 recursed;                // Number of times recursed during validation
+    mDNSu8 ValidationRequired;      // Copy of the question's ValidationRequired status
+    mDNSu8 InsecureProofDone;
+    mDNSu8 NumPackets;              // Number of packets that we send on the wire for DNSSEC verification.
+    mDNSs32 StartTime;              // Time the DNSSEC verification starts
     mDNSu32 flags;
     RRVerifierSet next;
     mDNSu32 flags;
     RRVerifierSet next;
-    domainname *wildcardName; // set if the answer is wildcard expanded
+    domainname *wildcardName;       // set if the answer is wildcard expanded
     RRVerifier *pendingNSEC;
     DNSSECVerifierCallback *DVCallback;
     DNSSECVerifier *parent;
     RRVerifier *pendingNSEC;
     DNSSECVerifierCallback *DVCallback;
     DNSSECVerifier *parent;
-    RRVerifier *rrset;      // rrset for which we have to verify
-    RRVerifier *rrsig;      // RRSIG for rrset
-    RRVerifier *key;        // DNSKEY for rrset
-    RRVerifier *rrsigKey;   // RRSIG for DNSKEY
-    RRVerifier *ds;         // DS for DNSKEY set in parent zone
+    RRVerifier *rrset;              // rrset for which we have to verify
+    RRVerifier *rrsig;              // RRSIG for rrset
+    RRVerifier *key;                // DNSKEY for rrset
+    RRVerifier *rrsigKey;           // RRSIG for DNSKEY
+    RRVerifier *ds;                 // DS for DNSKEY set in parent zone
+    AuthChain *saveac;
     AuthChain *ac;
     AuthChain **actail;
     AlgContext *ctx;
 };
 
     AuthChain *ac;
     AuthChain **actail;
     AlgContext *ctx;
 };
 
+
+struct InsecureContext_struct
+{
+    DNSSECVerifier *dv;             // dv for which we are doing the insecure proof
+    mDNSu8 skip;                    // labels to skip for forming the name from origName
+    DNSSECStatus status;            // status to deliver when done
+    mDNSu8 triggerLabelCount;       // Label count of the name that triggered the insecure proof
+    DNSQuestion q;
+};
+
 #define LogDNSSEC LogOperation
 
 #define DNS_SERIAL_GT(a, b) ((int)((a) - (b)) > 0)
 #define DNS_SERIAL_LT(a, b) ((int)((a) - (b)) < 0)
 
 #define LogDNSSEC LogOperation
 
 #define DNS_SERIAL_GT(a, b) ((int)((a) - (b)) > 0)
 #define DNS_SERIAL_LT(a, b) ((int)((a) - (b)) < 0)
 
-extern void StartDNSSECVerification(mDNS *const m, DNSSECVerifier *dv);
+extern void StartDNSSECVerification(mDNS *const m, void *context);
 extern RRVerifier* AllocateRRVerifier(const ResourceRecord *const rr, mStatus *status);
 extern mStatus AddRRSetToVerifier(DNSSECVerifier *dv, const ResourceRecord *const rr, RRVerifier *rv, RRVerifierSet set);
 extern void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion *q);
 extern void FreeDNSSECVerifier(mDNS *const m, DNSSECVerifier *dv);
 extern DNSSECVerifier *AllocateDNSSECVerifier(mDNS *const m, const domainname *name, mDNSu16 rrtype, mDNSInterfaceID InterfaceID,
 extern RRVerifier* AllocateRRVerifier(const ResourceRecord *const rr, mStatus *status);
 extern mStatus AddRRSetToVerifier(DNSSECVerifier *dv, const ResourceRecord *const rr, RRVerifier *rv, RRVerifierSet set);
 extern void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion *q);
 extern void FreeDNSSECVerifier(mDNS *const m, DNSSECVerifier *dv);
 extern DNSSECVerifier *AllocateDNSSECVerifier(mDNS *const m, const domainname *name, mDNSu16 rrtype, mDNSInterfaceID InterfaceID,
-                                              DNSSECVerifierCallback dvcallback, mDNSQuestionCallback qcallback);
+                                              mDNSu8 ValidationRequired, DNSSECVerifierCallback dvcallback, mDNSQuestionCallback qcallback);
 extern void InitializeQuestion(mDNS *const m, DNSQuestion *question, mDNSInterfaceID InterfaceID, const domainname *qname,
                                mDNSu16 qtype, mDNSQuestionCallback *callback, void *context);
 extern void ValidateRRSIG(DNSSECVerifier *dv, RRVerifierSet type, const ResourceRecord *const rr);
 extern void AuthChainLink(DNSSECVerifier *dv, AuthChain *ae);
 extern void InitializeQuestion(mDNS *const m, DNSQuestion *question, mDNSInterfaceID InterfaceID, const domainname *qname,
                                mDNSu16 qtype, mDNSQuestionCallback *callback, void *context);
 extern void ValidateRRSIG(DNSSECVerifier *dv, RRVerifierSet type, const ResourceRecord *const rr);
 extern void AuthChainLink(DNSSECVerifier *dv, AuthChain *ae);
+extern mStatus DNSNameToLowerCase(domainname *d, domainname *result);
+extern int DNSMemCmp(const mDNSu8 *const m1, const mDNSu8 *const m2, int len);
+extern int DNSSECCanonicalOrder(const domainname *const d1, const domainname *const d2, int *subdomain);
+extern void ProveInsecure(mDNS *const m, DNSSECVerifier *dv, InsecureContext *ic, domainname *trigger);
+extern void BumpDNSSECStats(mDNS *const m, DNSSECStatsAction action, DNSSECStatsType type, mDNSu32 value);
+extern char *DNSSECStatusName(DNSSECStatus status);
+
+// DNSSECProbe belongs in DNSSECSupport.h but then we don't want to expose yet another plaform specific dnssec file
+// to other platforms where dnssec is not supported.
+extern void DNSSECProbe(mDNS *const m);
 
 #endif // __DNSSEC_H
 
 #endif // __DNSSEC_H
index 4b333c9373933aab5bcfbeaef18f0da033d31f03..54fa735978b8bae66b806b944e8f199182a3d71b 100755 (executable)
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 4 -*-
  *
 /* -*- Mode: C; tab-width: 4 -*-
  *
- * Copyright (c) 2002-2012 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2002-2013 Apple Computer, 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.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@
 #include "uDNS.h"                       // Defines entry points into unicast-specific routines
 #include "nsec.h"
 #include "dnssec.h"
 #include "uDNS.h"                       // Defines entry points into unicast-specific routines
 #include "nsec.h"
 #include "dnssec.h"
+#include "anonymous.h"
 
 // Disable certain benign warnings with Microsoft compilers
 #if (defined(_MSC_VER))
 
 // Disable certain benign warnings with Microsoft compilers
 #if (defined(_MSC_VER))
@@ -43,6 +44,8 @@
     #pragma warning(disable:4706)
 #endif
 
     #pragma warning(disable:4706)
 #endif
 
+#include "dns_sd.h" // for kDNSServiceFlags* definitions
+
 #if APPLE_OSX_mDNSResponder
 
 #include <WebFilterDNS/WebFilterDNS.h>
 #if APPLE_OSX_mDNSResponder
 
 #include <WebFilterDNS/WebFilterDNS.h>
@@ -66,21 +69,28 @@ 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 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);
-mDNSlocal void mDNS_CheckForCachedNSECS(mDNS *const m, DNSQuestion *q);
+mDNSlocal void mDNS_PurgeForQuestion(mDNS *const m, DNSQuestion *q);
+mDNSlocal void CheckForDNSSECRecords(mDNS *const m, DNSQuestion *q);
 mDNSlocal void mDNS_SendKeepalives(mDNS *const m);
 mDNSlocal void mDNS_SendKeepalives(mDNS *const m);
-mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSAddr *laddr, mDNSAddr *raddr, mDNSu32 *seq,
-                                         mDNSu32 *ack, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu16 *win);
+mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSAddr *laddr, mDNSAddr *raddr, mDNSEthAddr *eth,
+                                         mDNSu32 *seq, mDNSu32 *ack, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu16 *win);
+
+mDNSlocal void AdvertiseAllInterfaceRecords(mDNS *const m);
+mDNSlocal void DeadvertiseAllInterfaceRecords(mDNS *const m);
+mDNSlocal void FreeNSECRecords(mDNS *const m, CacheRecord *NSECRecords);
+mDNSlocal void mDNSParseNSEC3Records(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end,
+                                        const mDNSInterfaceID InterfaceID, CacheRecord **NSEC3Records);
 
 
-#define mDNS_KeepaliveRecord(rr) ((rr)->rrtype == kDNSType_NULL && SameDomainLabel(SecondLabel((rr)->name)->c, (mDNSu8 *)"\x0A_keepalive"))
 
 // ***************************************************************************
 #if COMPILER_LIKES_PRAGMA_MARK
 #pragma mark - Program Constants
 #endif
 
 
 // ***************************************************************************
 #if COMPILER_LIKES_PRAGMA_MARK
 #pragma mark - Program Constants
 #endif
 
-#define NO_HINFO 1
+// To Turn OFF mDNS_Tracer set MDNS_TRACER to 0 or undef it
+#define MDNS_TRACER 1
 
 
+#define NO_HINFO 1
 
 // Any records bigger than this are considered 'large' records
 #define SmallRecordLimit 1024
 
 // Any records bigger than this are considered 'large' records
 #define SmallRecordLimit 1024
@@ -88,6 +98,21 @@ mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSA
 #define kMaxUpdateCredits 10
 #define kUpdateCreditRefreshInterval (mDNSPlatformOneSecond * 6)
 
 #define kMaxUpdateCredits 10
 #define kUpdateCreditRefreshInterval (mDNSPlatformOneSecond * 6)
 
+// define special NR_AnswerTo values
+#define NR_AnswerMulticast  (mDNSu8*)~0
+#define NR_AnswerUnicast    (mDNSu8*)~1
+
+// Defined to set the kDNSQClass_UnicastResponse bit in the first four query packets.
+// else, it's just set it the first query.
+#define mDNS_REQUEST_UNICAST_RESPONSE 0
+
+// The code (see SendQueries() and BuildQuestion()) needs to have the
+// RequestUnicast value set to a value one greater than the number of times you want the query
+// sent with the "request unicast response" (QU) bit set.
+#define SET_QU_IN_FIRST_QUERY   2
+#define SET_QU_IN_FIRST_FOUR_QUERIES   5
+
+
 mDNSexport const char *const mDNS_DomainTypeNames[] =
 {
     "b._dns-sd._udp.",      // Browse
 mDNSexport const char *const mDNS_DomainTypeNames[] =
 {
     "b._dns-sd._udp.",      // Browse
@@ -107,21 +132,19 @@ mDNSexport const char *const mDNS_DomainTypeNames[] =
 #pragma mark - General Utility Functions
 #endif
 
 #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 && \
+// Returns true if this is a  unique, authoritative LocalOnly record that answers questions of type 
+// A, AAAA , CNAME, or PTR.  The caller should answer the question with this record and not send out 
+// the question on the wire if LocalOnlyRecordAnswersQuestion() also returns true.
+// Main use is to handle /etc/hosts records and the LocalOnly PTR records created for localhost.
+#define UniqueLocalOnlyRecord(rr) ((rr)->ARType == AuthRecordLocalOnly && \
                                         (rr)->resrec.RecordType & kDNSRecordTypeUniqueMask && \
                                         ((rr)->resrec.rrtype == kDNSType_A || (rr)->resrec.rrtype == kDNSType_AAAA || \
                                         (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)
+                                         (rr)->resrec.rrtype == kDNSType_CNAME || \
+                                         (rr)->resrec.rrtype == kDNSType_PTR))
 
 mDNSlocal void SetNextQueryStopTime(mDNS *const m, const DNSQuestion *const q)
 {
 
 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);
+    mDNS_CheckLock(m);
 
 #if ForceAlerts
     if (m->mDNS_busy != m->mDNS_reentrancy+1) *(long*)0 = 0;
 
 #if ForceAlerts
     if (m->mDNS_busy != m->mDNS_reentrancy+1) *(long*)0 = 0;
@@ -133,8 +156,7 @@ mDNSlocal void SetNextQueryStopTime(mDNS *const m, const DNSQuestion *const q)
 
 mDNSexport void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q)
 {
 
 mDNSexport void SetNextQueryTime(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);
+    mDNS_CheckLock(m);
 
 #if ForceAlerts
     if (m->mDNS_busy != m->mDNS_reentrancy+1) *(long*)0 = 0;
 
 #if ForceAlerts
     if (m->mDNS_busy != m->mDNS_reentrancy+1) *(long*)0 = 0;
@@ -330,7 +352,7 @@ mDNSlocal CacheGroup *CacheGroupForRecord(const mDNS *const m, const mDNSu32 slo
     return(CacheGroupForName(m, slot, rr->namehash, rr->name));
 }
 
     return(CacheGroupForName(m, slot, rr->namehash, rr->name));
 }
 
-mDNSexport mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr)
+mDNSexport mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr, mDNSBool *myself)
 {
     NetworkInterfaceInfo *intf;
 
 {
     NetworkInterfaceInfo *intf;
 
@@ -341,19 +363,44 @@ mDNSexport mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterface
         for (intf = m->HostInterfaces; intf; intf = intf->next)
             if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx)
                 if (((intf->ip.ip.v4.NotAnInteger ^ addr->ip.v4.NotAnInteger) & intf->mask.ip.v4.NotAnInteger) == 0)
         for (intf = m->HostInterfaces; intf; intf = intf->next)
             if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx)
                 if (((intf->ip.ip.v4.NotAnInteger ^ addr->ip.v4.NotAnInteger) & intf->mask.ip.v4.NotAnInteger) == 0)
+                {
+                    if (myself)
+                    {
+                        if (mDNSSameIPv4Address(intf->ip.ip.v4, addr->ip.v4))
+                            *myself = mDNStrue;
+                        else
+                            *myself = mDNSfalse;
+                        if (*myself)
+                            debugf("mDNS_AddressIsLocalSubnet: IPv4 %#a returning true", addr);
+                        else
+                            debugf("mDNS_AddressIsLocalSubnet: IPv4 %#a returning false", addr);
+                    }
                     return(mDNStrue);
                     return(mDNStrue);
+                }
     }
 
     if (addr->type == mDNSAddrType_IPv6)
     {
     }
 
     if (addr->type == mDNSAddrType_IPv6)
     {
-        if (mDNSv6AddressIsLinkLocal(&addr->ip.v6)) return(mDNStrue);
         for (intf = m->HostInterfaces; intf; intf = intf->next)
             if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx)
                 if ((((intf->ip.ip.v6.l[0] ^ addr->ip.v6.l[0]) & intf->mask.ip.v6.l[0]) == 0) &&
                     (((intf->ip.ip.v6.l[1] ^ addr->ip.v6.l[1]) & intf->mask.ip.v6.l[1]) == 0) &&
                     (((intf->ip.ip.v6.l[2] ^ addr->ip.v6.l[2]) & intf->mask.ip.v6.l[2]) == 0) &&
                     (((intf->ip.ip.v6.l[3] ^ addr->ip.v6.l[3]) & intf->mask.ip.v6.l[3]) == 0))
         for (intf = m->HostInterfaces; intf; intf = intf->next)
             if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx)
                 if ((((intf->ip.ip.v6.l[0] ^ addr->ip.v6.l[0]) & intf->mask.ip.v6.l[0]) == 0) &&
                     (((intf->ip.ip.v6.l[1] ^ addr->ip.v6.l[1]) & intf->mask.ip.v6.l[1]) == 0) &&
                     (((intf->ip.ip.v6.l[2] ^ addr->ip.v6.l[2]) & intf->mask.ip.v6.l[2]) == 0) &&
                     (((intf->ip.ip.v6.l[3] ^ addr->ip.v6.l[3]) & intf->mask.ip.v6.l[3]) == 0))
-                    return(mDNStrue);
+                    {
+                        if (myself)
+                        {
+                            if (mDNSSameIPv6Address(intf->ip.ip.v6, addr->ip.v6))
+                                *myself = mDNStrue;
+                            else
+                                *myself = mDNSfalse;
+                            if (*myself)
+                                debugf("mDNS_AddressIsLocalSubnet: IPv6 %#a returning true", addr);
+                            else
+                                debugf("mDNS_AddressIsLocalSubnet: IPv6 %#a returning false", addr);
+                        }
+                        return(mDNStrue);
+                    }
     }
 
     return(mDNSfalse);
     }
 
     return(mDNSfalse);
@@ -366,6 +413,27 @@ mDNSlocal NetworkInterfaceInfo *FirstInterfaceForID(mDNS *const m, const mDNSInt
     return(intf);
 }
 
     return(intf);
 }
 
+mDNSlocal NetworkInterfaceInfo *FirstIPv4LLInterfaceForID(mDNS *const m, const mDNSInterfaceID InterfaceID)
+{
+    NetworkInterfaceInfo *intf;
+
+    if (!InterfaceID)
+        return mDNSNULL;
+
+    // Note: We don't check for InterfaceActive, as the active interface could be IPv6 and 
+    // we still want to find the first IPv4 Link-Local interface
+    for (intf = m->HostInterfaces; intf; intf = intf->next)
+    {
+        if (intf->InterfaceID == InterfaceID &&
+            intf->ip.type == mDNSAddrType_IPv4 && mDNSv4AddressIsLinkLocal(&intf->ip.ip.v4))
+        {
+            debugf("FirstIPv4LLInterfaceForID: found LL interface with address %.4a", &intf->ip.ip.v4);
+            return intf;
+        }
+    }
+    return (mDNSNULL);
+}
+
 mDNSexport char *InterfaceNameForID(mDNS *const m, const mDNSInterfaceID InterfaceID)
 {
     NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID);
 mDNSexport char *InterfaceNameForID(mDNS *const m, const mDNSInterfaceID InterfaceID)
 {
     NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID);
@@ -373,7 +441,7 @@ mDNSexport char *InterfaceNameForID(mDNS *const m, const mDNSInterfaceID Interfa
 }
 
 // Caller should hold the lock
 }
 
 // Caller should hold the lock
-mDNSlocal void GenerateNegativeResponse(mDNS *const m)
+mDNSlocal void GenerateNegativeResponse(mDNS *const m, QC_result qc)
 {
     DNSQuestion *q;
     if (!m->CurrentQuestion) { LogMsg("GenerateNegativeResponse: ERROR!! CurrentQuestion not set"); return; }
 {
     DNSQuestion *q;
     if (!m->CurrentQuestion) { LogMsg("GenerateNegativeResponse: ERROR!! CurrentQuestion not set"); return; }
@@ -388,7 +456,7 @@ mDNSlocal void GenerateNegativeResponse(mDNS *const m)
     //
     // The question may not have set Intermediates in which case we don't deliver negative responses. So, to force
     // through we use "QC_forceresponse".
     //
     // The question may not have set Intermediates in which case we don't deliver negative responses. So, to force
     // through we use "QC_forceresponse".
-    AnswerCurrentQuestionWithResourceRecord(m, &m->rec.r, QC_forceresponse);
+    AnswerCurrentQuestionWithResourceRecord(m, &m->rec.r, qc);
     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
     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
@@ -403,6 +471,22 @@ mDNSexport void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, Re
     else
     {
         const mDNSu32 c = q->CNAMEReferrals + 1;        // Stash a copy of the new q->CNAMEReferrals value
     else
     {
         const mDNSu32 c = q->CNAMEReferrals + 1;        // Stash a copy of the new q->CNAMEReferrals value
+        UDPSocket *sock = q->LocalSocket;
+        mDNSOpaque16 id = q->TargetQID;
+
+        // if there is a message waiting at the socket, we want to process that instead
+        // of throwing it away. If we have a CNAME response that answers
+        // both A and AAAA question and while answering it we don't want to throw
+        // away the response where the actual addresses are present.
+        if (mDNSPlatformPeekUDP(m, q->LocalSocket))
+        {
+            LogInfo("AnswerQuestionByFollowingCNAME: Preserving UDP socket for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+            q->LocalSocket = mDNSNULL;
+        }
+        else
+        {
+            sock = mDNSNULL;
+        }
 
         // 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,
 
         // 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,
@@ -438,6 +522,14 @@ mDNSexport void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, Re
         // 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;
         // 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;
+        if (sock)
+        {
+            // We have a message waiting and that should answer this question.
+            if (q->LocalSocket)
+                mDNSPlatformUDPClose(q->LocalSocket);
+            q->LocalSocket = sock;
+            q->TargetQID = id;
+        }
     }
 }
 
     }
 }
 
@@ -470,20 +562,13 @@ mDNSlocal void AnswerLocalQuestionWithLocalAuthRecord(mDNS *const m, AuthRecord
     if (q->QuestionCallback && !q->NoAnswer)
     {
         q->CurrentAnswers += AddRecord ? 1 : -1;
     if (q->QuestionCallback && !q->NoAnswer)
     {
         q->CurrentAnswers += AddRecord ? 1 : -1;
-        if (LORecordAnswersAddressType(rr))
+        if (UniqueLocalOnlyRecord(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;
         {
             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;
-                // We can't possibly validate the entries in /etc/hosts. Hence, we
-                // report it as insecure.
-                if (q->ValidationRequired)
-                {
-                    q->ValidationStatus = DNSSEC_Insecure;
-                    q->ValidationState = DNSSECValDone;
-                }
                 q->QuestionCallback(m, q, &rr->resrec, AddRecord);
             }
             mDNS_ReclaimLockAfterCallback();    // Decrement mDNS_reentrancy to block mDNS API calls again
                 q->QuestionCallback(m, q, &rr->resrec, AddRecord);
             }
             mDNS_ReclaimLockAfterCallback();    // Decrement mDNS_reentrancy to block mDNS API calls again
@@ -495,11 +580,6 @@ mDNSlocal void AnswerLocalQuestionWithLocalAuthRecord(mDNS *const m, AuthRecord
         }
         else
         {
         }
         else
         {
-            if (q->ValidationRequired)
-            {
-                q->ValidationStatus = DNSSEC_Insecure;
-                q->ValidationState = DNSSECValDone;
-            }
             q->QuestionCallback(m, q, &rr->resrec, AddRecord);
         }
     }
             q->QuestionCallback(m, q, &rr->resrec, AddRecord);
         }
     }
@@ -589,13 +669,17 @@ mDNSlocal void AnswerAllLocalQuestionsWithLocalAuthRecord(mDNS *const m, AuthRec
 #define DefaultProbeCountForTypeUnique ((mDNSu8)3)
 #define DefaultProbeCountForRecordType(X)      ((X) == kDNSRecordTypeUnique ? DefaultProbeCountForTypeUnique : (mDNSu8)0)
 
 #define DefaultProbeCountForTypeUnique ((mDNSu8)3)
 #define DefaultProbeCountForRecordType(X)      ((X) == kDNSRecordTypeUnique ? DefaultProbeCountForTypeUnique : (mDNSu8)0)
 
-#define InitialAnnounceCount ((mDNSu8)8)
+// See RFC 6762: "8.3 Announcing"
+// "The Multicast DNS responder MUST send at least two unsolicited responses, one second apart."
+// Send 4, which is really 8 since we send on both IPv4 and IPv6.
+#define InitialAnnounceCount ((mDNSu8)4)
 
 // For goodbye packets we set the count to 3, and for wakeups we set it to 18
 // (which will be up to 15 wakeup attempts over the course of 30 seconds,
 // and then if the machine fails to wake, 3 goodbye packets).
 #define GoodbyeCount ((mDNSu8)3)
 #define WakeupCount ((mDNSu8)18)
 
 // For goodbye packets we set the count to 3, and for wakeups we set it to 18
 // (which will be up to 15 wakeup attempts over the course of 30 seconds,
 // and then if the machine fails to wake, 3 goodbye packets).
 #define GoodbyeCount ((mDNSu8)3)
 #define WakeupCount ((mDNSu8)18)
+#define MAX_PROBE_RESTARTS ((mDNSu8)20)
 
 // Number of wakeups we send if WakeOnResolve is set in the question
 #define InitialWakeOnResolveCount ((mDNSu8)3)
 
 // Number of wakeups we send if WakeOnResolve is set in the question
 #define InitialWakeOnResolveCount ((mDNSu8)3)
@@ -617,6 +701,18 @@ mDNSlocal void AnswerAllLocalQuestionsWithLocalAuthRecord(mDNS *const m, AuthRec
 #define TicksTTL(RR) ((mDNSs32)(RR)->resrec.rroriginalttl * mDNSPlatformOneSecond)
 #define RRExpireTime(RR) ((RR)->TimeRcvd + TicksTTL(RR))
 
 #define TicksTTL(RR) ((mDNSs32)(RR)->resrec.rroriginalttl * mDNSPlatformOneSecond)
 #define RRExpireTime(RR) ((RR)->TimeRcvd + TicksTTL(RR))
 
+// Adjustment factor to avoid race condition (used for unicast cache entries) :
+// Suppose real record has TTL of 3600, and our local caching server has held it for 3500 seconds, so it returns an aged TTL of 100.
+// If we do our normal refresh at 80% of the TTL, our local caching server will return 20 seconds, so we'll do another
+// 80% refresh after 16 seconds, and then the server will return 4 seconds, and so on, in the fashion of Zeno's paradox.
+// To avoid this, we extend the record's effective TTL to give it a little extra grace period.
+// We adjust the 100 second TTL to 127. This means that when we do our 80% query at 102 seconds,
+// the cached copy at our local caching server will already have expired, so the server will be forced
+// to fetch a fresh copy from the authoritative server, and then return a fresh record with the full TTL of 3600 seconds.
+
+#define RRAdjustTTL(ttl) ((ttl) + ((ttl)/4) + 2)
+#define RRUnadjustedTTL(ttl) ((((ttl) - 2) * 4) / 5)
+
 #define MaxUnansweredQueries 4
 
 // SameResourceRecordSignature returns true if two resources records have the same name, type, and class, and may be sent
 #define MaxUnansweredQueries 4
 
 // SameResourceRecordSignature returns true if two resources records have the same name, type, and class, and may be sent
@@ -784,16 +880,8 @@ mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr)
     // and we can begin broadcasting our announcements to take over ownership of that IP address.
     // If we don't wait for the client to go to sleep, then when the client sees our ARP Announcements there's a risk
     // (depending on the OS and networking stack it's using) that it might interpret it as a conflict and change its IP address.
     // and we can begin broadcasting our announcements to take over ownership of that IP address.
     // If we don't wait for the client to go to sleep, then when the client sees our ARP Announcements there's a risk
     // (depending on the OS and networking stack it's using) that it might interpret it as a conflict and change its IP address.
-    if (rr->AddressProxy.type) rr->LastAPTime = m->timenow;
-
-    // Unsolicited Neighbor Advertisements (RFC 2461 Section 7.2.6) give us fast address cache updating,
-    // but some older IPv6 clients get confused by them, so for now we don't send them. Without Unsolicited
-    // Neighbor Advertisements we have to rely on Neighbor Unreachability Detection instead, which is slower.
-    // Given this, we'll do our best to wake for existing IPv6 connections, but we don't want to encourage
-    // new ones for sleeping clients, so we'll we send deletions for our SPS clients' AAAA records.
-    if (m->KnownBugs & mDNS_KnownBug_LimitedIPv6)
-        if (rr->WakeUp.HMAC.l[0] && rr->resrec.rrtype == kDNSType_AAAA)
-            rr->LastAPTime = m->timenow - rr->ThisAPInterval + mDNSPlatformOneSecond * 10;
+    if (rr->AddressProxy.type) 
+        rr->LastAPTime = m->timenow;
 
     // Set LastMCTime to now, to inhibit multicast responses
     // (no need to send additional multicast responses when we're announcing anyway)
 
     // Set LastMCTime to now, to inhibit multicast responses
     // (no need to send additional multicast responses when we're announcing anyway)
@@ -868,6 +956,7 @@ mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr)
 
         rr->AnnounceCount  = InitialAnnounceCount;
         rr->RequireGoodbye = mDNSfalse;
 
         rr->AnnounceCount  = InitialAnnounceCount;
         rr->RequireGoodbye = mDNSfalse;
+        rr->ProbeRestartCount = 0;
         InitializeLastAPTime(m, rr);
     }
 }
         InitializeLastAPTime(m, rr);
     }
 }
@@ -934,6 +1023,7 @@ mDNSexport void ActivateUnicastRegistration(mDNS *const m, AuthRecord *const rr)
         rr->state = regState_Pending;
     }
     rr->ProbeCount     = 0;
         rr->state = regState_Pending;
     }
     rr->ProbeCount     = 0;
+    rr->ProbeRestartCount = 0;
     rr->AnnounceCount  = 0;
     rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL;
     rr->LastAPTime     = m->timenow - rr->ThisAPInterval;
     rr->AnnounceCount  = 0;
     rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL;
     rr->LastAPTime     = m->timenow - rr->ThisAPInterval;
@@ -1038,6 +1128,52 @@ mDNSlocal AuthRecord *CheckAuthSameRecord(AuthHash *r, AuthRecord *rr)
     return (mDNSNULL);
 }
 
     return (mDNSNULL);
 }
 
+
+mDNSlocal void DecrementAutoTargetServices(mDNS *const m, AuthRecord *const rr)
+{
+    if (!AuthRecord_uDNS(rr) && rr->resrec.rrtype == kDNSType_SRV && rr->AutoTarget == Target_AutoHost)
+    {
+        m->AutoTargetServices--;
+        LogInfo("DecrementAutoTargetServices: AutoService Record %s, AutoTargetServices %d", ARDisplayString(m, rr), m->AutoTargetServices);
+        if (!m->AutoTargetServices)
+            DeadvertiseAllInterfaceRecords(m);
+    }
+}
+
+mDNSlocal void IncrementAutoTargetServices(mDNS *const m, AuthRecord *const rr)
+{
+    if (!AuthRecord_uDNS(rr) && rr->resrec.rrtype == kDNSType_SRV && rr->AutoTarget == Target_AutoHost)
+    {
+        int count = m->AutoTargetServices;
+
+        // Bump up before calling AdvertiseAllInterfaceRecords. AdvertiseInterface
+        // returns without doing anything if the count is zero.
+        m->AutoTargetServices++;
+        LogInfo("IncrementAutoTargetServices: AutoService Record %s, AutoTargetServices %d", ARDisplayString(m, rr), m->AutoTargetServices);
+        if (!count)
+            AdvertiseAllInterfaceRecords(m);
+    }
+}
+
+mDNSlocal void getKeepaliveRaddr(mDNS *const m, AuthRecord *rr, mDNSAddr *raddr)
+{
+    mDNSAddr     laddr;
+    mDNSEthAddr  eth;
+    mDNSIPPort   lport, rport;
+    mDNSu32      timeout, seq, ack;
+    mDNSu16      win;
+
+    if (mDNS_KeepaliveRecord(&rr->resrec))
+    {
+        mDNS_ExtractKeepaliveInfo(rr, &timeout, &laddr, raddr, &eth, &seq, &ack, &lport, &rport, &win);
+        if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(raddr) || mDNSIPPortIsZero(lport) || mDNSIPPortIsZero(rport))
+        {
+            LogMsg("getKeepaliveRaddr: not a valid record %s for keepalive %#a:%d %#a:%d", ARDisplayString(m, rr), &laddr, lport.NotAnInteger, raddr, rport.NotAnInteger);
+            return;
+        }
+    }
+}
+
 // Exported so uDNS.c can call this
 mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr)
 {
 // Exported so uDNS.c can call this
 mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr)
 {
@@ -1152,6 +1288,7 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr)
     // Field Group 3: Transient state for Authoritative Records
     rr->Acknowledged      = mDNSfalse;
     rr->ProbeCount        = DefaultProbeCountForRecordType(rr->resrec.RecordType);
     // Field Group 3: Transient state for Authoritative Records
     rr->Acknowledged      = mDNSfalse;
     rr->ProbeCount        = DefaultProbeCountForRecordType(rr->resrec.RecordType);
+    rr->ProbeRestartCount = 0;
     rr->AnnounceCount     = InitialAnnounceCount;
     rr->RequireGoodbye    = mDNSfalse;
     rr->AnsweredLocalQ    = mDNSfalse;
     rr->AnnounceCount     = InitialAnnounceCount;
     rr->RequireGoodbye    = mDNSfalse;
     rr->AnsweredLocalQ    = mDNSfalse;
@@ -1274,6 +1411,7 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr)
         *p = rr;
         if (rr->resrec.RecordType == kDNSRecordTypeUnique) rr->resrec.RecordType = kDNSRecordTypeVerified;
         rr->ProbeCount    = 0;
         *p = rr;
         if (rr->resrec.RecordType == kDNSRecordTypeUnique) rr->resrec.RecordType = kDNSRecordTypeVerified;
         rr->ProbeCount    = 0;
+        rr->ProbeRestartCount = 0;
         rr->AnnounceCount = 0;
         if (rr->state != regState_NoTarget) ActivateUnicastRegistration(m, rr);
         return(mStatus_NoError);            // <--- Note: For unicast records, code currently bails out at this point
         rr->AnnounceCount = 0;
         if (rr->state != regState_NoTarget) ActivateUnicastRegistration(m, rr);
         return(mStatus_NoError);            // <--- Note: For unicast records, code currently bails out at this point
@@ -1284,6 +1422,7 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr)
     if (RRLocalOnly(rr))
     {
         rr->ProbeCount    = 0;
     if (RRLocalOnly(rr))
     {
         rr->ProbeCount    = 0;
+        rr->ProbeRestartCount = 0;
         rr->AnnounceCount = 0;
         r = CheckAuthIdenticalRecord(&m->rrauth, rr);
     }
         rr->AnnounceCount = 0;
         r = CheckAuthIdenticalRecord(&m->rrauth, rr);
     }
@@ -1331,8 +1470,25 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr)
         }
     }
 
         }
     }
 
+    // If this is a keepalive record, fetch the MAC address of the remote host.
+    // This is used by the in-NIC proxy to send the keepalive packets.
+    if (mDNS_KeepaliveRecord(&rr->resrec))
+    {
+        // Set the record type to known unique to prevent probing keep alive records.
+        // Also make sure we do not announce the keepalive records.
+       rr->resrec.RecordType = kDNSRecordTypeKnownUnique;
+       rr->AnnounceCount     = 0;
+       mDNSAddr   raddr;
+       getKeepaliveRaddr(m, rr, &raddr);
+       // This is an asynchronous call. Once the remote MAC address is available, helper will schedule an
+       // asynchronous task to update the resource record
+       mDNSPlatformGetRemoteMacAddr(m, &raddr);
+    }
+
     if (!AuthRecord_uDNS(rr))   // This check is superfluous, given that for unicast records we (currently) bail out above
     {
     if (!AuthRecord_uDNS(rr))   // This check is superfluous, given that for unicast records we (currently) bail out above
     {
+        // We have inserted the record in the list. See if we have to advertise the A/AAAA,HINFO,PTR records.
+        IncrementAutoTargetServices(m, rr);
         // For records that are not going to probe, acknowledge them right away
         if (rr->resrec.RecordType != kDNSRecordTypeUnique && rr->resrec.RecordType != kDNSRecordTypeDeregistering)
             AcknowledgeRecord(m, rr);
         // For records that are not going to probe, acknowledge them right away
         if (rr->resrec.RecordType != kDNSRecordTypeUnique && rr->resrec.RecordType != kDNSRecordTypeDeregistering)
             AcknowledgeRecord(m, rr);
@@ -1434,6 +1590,7 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr,
                 }
                 dup->resrec.RecordType        = rr->resrec.RecordType;
                 dup->ProbeCount      = rr->ProbeCount;
                 }
                 dup->resrec.RecordType        = rr->resrec.RecordType;
                 dup->ProbeCount      = rr->ProbeCount;
+                dup->ProbeRestartCount = rr->ProbeRestartCount;
                 dup->AnnounceCount   = rr->AnnounceCount;
                 dup->RequireGoodbye  = rr->RequireGoodbye;
                 dup->AnsweredLocalQ  = rr->AnsweredLocalQ;
                 dup->AnnounceCount   = rr->AnnounceCount;
                 dup->RequireGoodbye  = rr->RequireGoodbye;
                 dup->AnsweredLocalQ  = rr->AnsweredLocalQ;
@@ -1467,8 +1624,9 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr,
             // deregister (unilink from the DuplicateRecords list), we will be recursing infinitely. Hence,
             // clear the HMAC which will cause it to deregister. See <rdar://problem/10380988> for
             // details.
             // deregister (unilink from the DuplicateRecords list), we will be recursing infinitely. Hence,
             // clear the HMAC which will cause it to deregister. See <rdar://problem/10380988> for
             // details.
-            rr->WakeUp.HMAC = zeroEthAddr;
+            rr->WakeUp.HMAC    = zeroEthAddr;
             rr->RequireGoodbye = mDNSfalse;
             rr->RequireGoodbye = mDNSfalse;
+            rr->resrec.RecordType = kDNSRecordTypeDeregistering;
             dupList = mDNStrue;
         }
         if (*p) debugf("mDNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)",
             dupList = mDNStrue;
         }
         if (*p) debugf("mDNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)",
@@ -1572,6 +1730,7 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr,
         {
             *p = rr->next;                  // Cut this record from the list
             if (m->NewLocalRecords == rr) m->NewLocalRecords = rr->next;
         {
             *p = rr->next;                  // Cut this record from the list
             if (m->NewLocalRecords == rr) m->NewLocalRecords = rr->next;
+            DecrementAutoTargetServices(m, rr);
         }
         // If someone is about to look at this, bump the pointer forward
         if (m->CurrentRecord   == rr) m->CurrentRecord   = rr->next;
         }
         // If someone is about to look at this, bump the pointer forward
         if (m->CurrentRecord   == rr) m->CurrentRecord   = rr->next;
@@ -1697,12 +1856,21 @@ mDNSlocal void AddAdditionalsToResponseList(mDNS *const m, AuthRecord *ResponseR
     }
 }
 
     }
 }
 
+mDNSlocal int AnonInfoSpace(AnonymousInfo *info)
+{
+    ResourceRecord *rr = info->nsec3RR;
+
+    // 2 bytes for compressed name + type (2) class (2) TTL (4) rdlength (2) rdata (n)
+    return (2 + 10 + rr->rdlength);
+}
+
 mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const dest, const mDNSInterfaceID InterfaceID)
 {
     AuthRecord *rr;
     AuthRecord  *ResponseRecords = mDNSNULL;
     AuthRecord **nrp             = &ResponseRecords;
     NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID);
 mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const dest, const mDNSInterfaceID InterfaceID)
 {
     AuthRecord *rr;
     AuthRecord  *ResponseRecords = mDNSNULL;
     AuthRecord **nrp             = &ResponseRecords;
     NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID);
+    int AnoninfoSpace = 0;
 
     // Make a list of all our records that need to be unicast to this destination
     for (rr = m->ResourceRecords; rr; rr=rr->next)
 
     // Make a list of all our records that need to be unicast to this destination
     for (rr = m->ResourceRecords; rr; rr=rr->next)
@@ -1731,7 +1899,11 @@ mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const d
                 }
 
                 if (rr->NextResponse == mDNSNULL && nrp != &rr->NextResponse)   // rr->NR_AnswerTo
                 }
 
                 if (rr->NextResponse == mDNSNULL && nrp != &rr->NextResponse)   // rr->NR_AnswerTo
-                { rr->NR_AnswerTo = (mDNSu8*)~0; *nrp = rr; nrp = &rr->NextResponse; }
+                {
+                    rr->NR_AnswerTo = NR_AnswerMulticast;
+                    *nrp = rr;
+                    nrp = &rr->NextResponse;
+                }
             }
         }
     }
             }
         }
     }
@@ -1748,11 +1920,23 @@ mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const d
         while (ResponseRecords && ResponseRecords->NR_AnswerTo)
         {
             rr = ResponseRecords;
         while (ResponseRecords && ResponseRecords->NR_AnswerTo)
         {
             rr = ResponseRecords;
+            if (rr->resrec.AnonInfo)
+            {
+                AnoninfoSpace += AnonInfoSpace(rr->resrec.AnonInfo);
+                rr->resrec.AnonInfo->SendNow = mDNSInterfaceMark;
+            }
             if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
                 rr->resrec.rrclass |= kDNSClass_UniqueRRSet;        // Temporarily set the cache flush bit so PutResourceRecord will set it
             if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
                 rr->resrec.rrclass |= kDNSClass_UniqueRRSet;        // Temporarily set the cache flush bit so PutResourceRecord will set it
-            newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec);
+
+            // Retract the limit by AnoninfoSpace which we need to put the AnoInfo option.
+            newptr = PutResourceRecordTTLWithLimit(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, rr->resrec.rroriginalttl,
+                 m->omsg.data + (AllowedRRSpace(&m->omsg) - AnoninfoSpace));
+
             rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet;           // Make sure to clear cache flush bit back to normal state
             rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet;           // Make sure to clear cache flush bit back to normal state
-            if (!newptr && m->omsg.h.numAnswers) break; // If packet full, send it now
+            if (!newptr && m->omsg.h.numAnswers)
+            {
+                break; // If packet full, send it now
+            }
             if (newptr) responseptr = newptr;
             ResponseRecords = rr->NextResponse;
             rr->NextResponse    = mDNSNULL;
             if (newptr) responseptr = newptr;
             ResponseRecords = rr->NextResponse;
             rr->NextResponse    = mDNSNULL;
@@ -1761,6 +1945,29 @@ mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const d
             rr->RequireGoodbye  = mDNStrue;
         }
 
             rr->RequireGoodbye  = mDNStrue;
         }
 
+        // We have reserved the space for AnonInfo option. PutResourceRecord uses the
+        // standard limit (AllowedRRSpace) and we should have space now.
+        for (rr = m->ResourceRecords; rr; rr=rr->next)
+        {
+            if (rr->resrec.AnonInfo && rr->resrec.AnonInfo->SendNow == mDNSInterfaceMark)
+            {
+                ResourceRecord *nsec3RR = rr->resrec.AnonInfo->nsec3RR;
+
+                newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAuthorities, nsec3RR);
+                if (newptr)
+                {
+                    responseptr = newptr;
+                    debugf("SendDelayedUnicastResponse: Added NSEC3 Record %s on %p", RRDisplayString(m, nsec3RR), intf->InterfaceID);
+                }
+                else
+                {
+                    // We allocated space and we should not fail. Don't break, we need to clear the SendNow flag.
+                    LogMsg("SendDelayedUnicastResponse: ERROR!! Cannot Add NSEC3 Record %s on %p", RRDisplayString(m, nsec3RR), intf->InterfaceID);
+                }
+                rr->resrec.AnonInfo->SendNow = mDNSNULL;
+            }
+        }
+
         // Add additionals, if there's space
         while (ResponseRecords && !ResponseRecords->NR_AnswerTo)
         {
         // Add additionals, if there's space
         while (ResponseRecords && !ResponseRecords->NR_AnswerTo)
         {
@@ -2041,6 +2248,15 @@ mDNSlocal void SendNDP(mDNS *const m, const mDNSu8 op, const mDNSu8 flags, const
     mDNSPlatformSendRawPacket(m->omsg.data, ptr, rr->resrec.InterfaceID);
 }
 
     mDNSPlatformSendRawPacket(m->omsg.data, ptr, rr->resrec.InterfaceID);
 }
 
+mDNSlocal void SetupTracerOpt(const mDNS *const m, rdataOPT *const Trace)
+{
+    Trace->u.tracer.platf   = m->mDNS_plat;
+    Trace->u.tracer.mDNSv   = _DNS_SD_H/1000; 
+
+    Trace->opt              = kDNSOpt_Trace;
+    Trace->optlen           = DNSOpt_TraceData_Space - 4;
+}
+
 mDNSlocal void SetupOwnerOpt(const mDNS *const m, const NetworkInterfaceInfo *const intf, rdataOPT *const owner)
 {
     owner->u.owner.vers     = 0;
 mDNSlocal void SetupOwnerOpt(const mDNS *const m, const NetworkInterfaceInfo *const intf, rdataOPT *const owner)
 {
     owner->u.owner.vers     = 0;
@@ -2061,6 +2277,36 @@ mDNSlocal void GrantUpdateCredit(AuthRecord *rr)
     else rr->NextUpdateCredit = NonZeroTime(rr->NextUpdateCredit + kUpdateCreditRefreshInterval);
 }
 
     else rr->NextUpdateCredit = NonZeroTime(rr->NextUpdateCredit + kUpdateCreditRefreshInterval);
 }
 
+mDNSlocal mDNSBool ShouldSendGoodbyesBeforeSleep(mDNS *const m, const NetworkInterfaceInfo *intf, AuthRecord *rr)
+{
+    // If there are no sleep proxies, we set the state to SleepState_Sleeping explicitly
+    // and hence there is no need to check for Transfering state. But if we have sleep
+    // proxies and partially sending goodbyes for some records, we will be in Transfering
+    // state and hence need to make sure that we send goodbyes in that case too. Checking whether
+    // we are not awake handles both cases.
+    if ((rr->AuthFlags & AuthFlagsWakeOnly) && (m->SleepState != SleepState_Awake))
+    {
+        debugf("ShouldSendGoodbyesBeforeSleep: marking for goodbye", ARDisplayString(m, rr));
+        return mDNStrue;
+    }
+
+    if (m->SleepState != SleepState_Sleeping)
+        return mDNSfalse;
+
+    // If we are going to sleep and in SleepState_Sleeping, SendGoodbyes on the interface tell you
+    // whether you can send goodbyes or not.
+    if (!intf->SendGoodbyes)
+    {
+        debugf("ShouldSendGoodbyesBeforeSleep: not sending goodbye %s, int %p", ARDisplayString(m, rr), intf->InterfaceID);
+        return mDNSfalse;
+    }
+    else
+    {
+        debugf("ShouldSendGoodbyesBeforeSleep: sending goodbye %s, int %p", ARDisplayString(m, rr), intf->InterfaceID);
+        return mDNStrue;
+    }
+}
+
 // Note about acceleration of announcements to facilitate automatic coalescing of
 // multiple independent threads of announcements into a single synchronized thread:
 // The announcements in the packet may be at different stages of maturity;
 // Note about acceleration of announcements to facilitate automatic coalescing of
 // multiple independent threads of announcements into a single synchronized thread:
 // The announcements in the packet may be at different stages of maturity;
@@ -2125,7 +2371,8 @@ mDNSlocal void SendResponses(mDNS *const m)
                     LogSPS("SendResponses: Sending wakeup %2d for %.6a %s", rr->AnnounceCount-3, &rr->WakeUp.IMAC, ARDisplayString(m, rr));
                     SendWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.IMAC, &rr->WakeUp.password);
                     for (r2 = rr; r2; r2=r2->next)
                     LogSPS("SendResponses: Sending wakeup %2d for %.6a %s", rr->AnnounceCount-3, &rr->WakeUp.IMAC, ARDisplayString(m, rr));
                     SendWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.IMAC, &rr->WakeUp.password);
                     for (r2 = rr; r2; r2=r2->next)
-                        if (r2->AnnounceCount && r2->resrec.InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&r2->WakeUp.IMAC, &rr->WakeUp.IMAC))
+                        if (r2->AnnounceCount && r2->resrec.InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&r2->WakeUp.IMAC, &rr->WakeUp.IMAC) &&
+                                !mDNSSameEthAddress(&zeroEthAddr, &r2->WakeUp.HMAC))
                         {
                             // For now we only want to send a single Unsolicited Neighbor Advertisement restoring the address to the original
                             // owner, because these packets can cause some IPv6 stacks to falsely conclude that there's an address conflict.
                         {
                             // For now we only want to send a single Unsolicited Neighbor Advertisement restoring the address to the original
                             // owner, because these packets can cause some IPv6 stacks to falsely conclude that there's an address conflict.
@@ -2145,20 +2392,23 @@ mDNSlocal void SendResponses(mDNS *const m)
             {
                 if (rr->AddressProxy.type)
                 {
             {
                 if (rr->AddressProxy.type)
                 {
-                    rr->AnnounceCount--;
-                    rr->ThisAPInterval *= 2;
-                    rr->LastAPTime = m->timenow;
-                    if (rr->AddressProxy.type == mDNSAddrType_IPv4)
-                    {
-                        LogSPS("ARP Announcement %2d Capturing traffic for H-MAC %.6a I-MAC %.6a %s",
-                               rr->AnnounceCount, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m,rr));
-                        SendARP(m, 1, rr, &rr->AddressProxy.ip.v4, &zeroEthAddr, &rr->AddressProxy.ip.v4, &onesEthAddr);
-                    }
-                    else if (rr->AddressProxy.type == mDNSAddrType_IPv6)
+                    if (!mDNSSameEthAddress(&zeroEthAddr, &rr->WakeUp.HMAC))
                     {
                     {
-                        LogSPS("NDP Announcement %2d Capturing traffic for H-MAC %.6a I-MAC %.6a %s",
-                               rr->AnnounceCount, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m,rr));
-                        SendNDP(m, NDP_Adv, NDP_Override, rr, &rr->AddressProxy.ip.v6, mDNSNULL, &AllHosts_v6, &AllHosts_v6_Eth);
+                        rr->AnnounceCount--;
+                        rr->ThisAPInterval *= 2;
+                        rr->LastAPTime = m->timenow;
+                        if (rr->AddressProxy.type == mDNSAddrType_IPv4)
+                        {
+                            LogSPS("ARP Announcement %2d Capturing traffic for H-MAC %.6a I-MAC %.6a %s",
+                                    rr->AnnounceCount, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m,rr));
+                            SendARP(m, 1, rr, &rr->AddressProxy.ip.v4, &zeroEthAddr, &rr->AddressProxy.ip.v4, &onesEthAddr);
+                        }
+                        else if (rr->AddressProxy.type == mDNSAddrType_IPv6)
+                        {
+                            LogSPS("NDP Announcement %2d Capturing traffic for H-MAC %.6a I-MAC %.6a %s",
+                                    rr->AnnounceCount, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m,rr));
+                            SendNDP(m, NDP_Adv, NDP_Override, rr, &rr->AddressProxy.ip.v6, mDNSNULL, &AllHosts_v6, &AllHosts_v6_Eth);
+                        }
                     }
                 }
                 else
                     }
                 }
                 else
@@ -2270,10 +2520,12 @@ mDNSlocal void SendResponses(mDNS *const m)
 
     while (intf)
     {
 
     while (intf)
     {
-        const int OwnerRecordSpace = (m->AnnounceOwner && intf->MAC.l[0]) ? DNSOpt_Header_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC) : 0;
+        int OwnerRecordSpace = (m->AnnounceOwner && intf->MAC.l[0]) ? DNSOpt_Header_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC) : 0;
+        int TraceRecordSpace = (mDNS_McastTracingEnabled && MDNS_TRACER) ? DNSOpt_Header_Space + DNSOpt_TraceData_Space : 0;
         int numDereg    = 0;
         int numAnnounce = 0;
         int numAnswer   = 0;
         int numDereg    = 0;
         int numAnnounce = 0;
         int numAnswer   = 0;
+        int AnoninfoSpace = 0;
         mDNSu8 *responseptr = m->omsg.data;
         mDNSu8 *newptr;
         InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags);
         mDNSu8 *responseptr = m->omsg.data;
         mDNSu8 *newptr;
         InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags);
@@ -2298,8 +2550,7 @@ mDNSlocal void SendResponses(mDNS *const m)
                 RData  *OldRData    = rr->resrec.rdata;
                 mDNSu16 oldrdlength = rr->resrec.rdlength;
                 mDNSu8 active = (mDNSu8)
                 RData  *OldRData    = rr->resrec.rdata;
                 mDNSu16 oldrdlength = rr->resrec.rdlength;
                 mDNSu8 active = (mDNSu8)
-                                (rr->resrec.RecordType != kDNSRecordTypeDeregistering &&
-                                 (m->SleepState != SleepState_Sleeping || intf->SPSAddr[0].type || intf->SPSAddr[1].type || intf->SPSAddr[2].type));
+                                (rr->resrec.RecordType != kDNSRecordTypeDeregistering && !ShouldSendGoodbyesBeforeSleep(m, intf, rr));
                 newptr = mDNSNULL;
                 if (rr->NewRData && active)
                 {
                 newptr = mDNSNULL;
                 if (rr->NewRData && active)
                 {
@@ -2313,6 +2564,17 @@ mDNSlocal void SendResponses(mDNS *const m)
                     SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength);
                 }
 
                     SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength);
                 }
 
+                if (rr->resrec.AnonInfo)
+                {
+                    int tmp = AnonInfoSpace(rr->resrec.AnonInfo);
+
+                    AnoninfoSpace += tmp;
+                    // Adjust OwnerRecordSpace/TraceRecordSpace which is used by PutRR_OS_TTL below so that
+                    // we have space to put in the NSEC3 record in the authority section.
+                    OwnerRecordSpace += tmp;
+                    TraceRecordSpace += tmp;
+                }
+
                 if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
                     rr->resrec.rrclass |= kDNSClass_UniqueRRSet;        // Temporarily set the cache flush bit so PutResourceRecord will set it
                 newptr = PutRR_OS_TTL(responseptr, &m->omsg.h.numAnswers, &rr->resrec, active ? rr->resrec.rroriginalttl : 0);
                 if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
                     rr->resrec.rrclass |= kDNSClass_UniqueRRSet;        // Temporarily set the cache flush bit so PutResourceRecord will set it
                 newptr = PutRR_OS_TTL(responseptr, &m->omsg.h.numAnswers, &rr->resrec, active ? rr->resrec.rroriginalttl : 0);
@@ -2335,6 +2597,13 @@ mDNSlocal void SendResponses(mDNS *const m)
 
                 if (newptr)     // If succeeded in sending, advance to next interface
                 {
 
                 if (newptr)     // If succeeded in sending, advance to next interface
                 {
+                    if (rr->resrec.AnonInfo)
+                    {
+                        debugf("SendResponses: Marking %s, OwnerRecordSpace %d, TraceRecordSpace %d, limit %p", ARDisplayString(m, rr), OwnerRecordSpace,
+                                TraceRecordSpace, m->omsg.data + AllowedRRSpace(&m->omsg) - OwnerRecordSpace - TraceRecordSpace);
+                        rr->resrec.AnonInfo->SendNow = intf->InterfaceID;
+                    }
+
                     // If sending on all interfaces, go to next interface; else we're finished now
                     if (rr->ImmedAnswer == mDNSInterfaceMark && rr->resrec.InterfaceID == mDNSInterface_Any)
                         rr->SendRNow = GetNextActiveInterfaceID(intf);
                     // If sending on all interfaces, go to next interface; else we're finished now
                     if (rr->ImmedAnswer == mDNSInterfaceMark && rr->resrec.InterfaceID == mDNSInterface_Any)
                         rr->SendRNow = GetNextActiveInterfaceID(intf);
@@ -2344,6 +2613,31 @@ mDNSlocal void SendResponses(mDNS *const m)
             }
         }
 
             }
         }
 
+        // Get the reserved space back
+        OwnerRecordSpace -= AnoninfoSpace;
+        TraceRecordSpace -= AnoninfoSpace; 
+        newptr = responseptr;
+        for (rr = m->ResourceRecords; rr; rr=rr->next)
+        {
+            if (rr->resrec.AnonInfo && rr->resrec.AnonInfo->SendNow == intf->InterfaceID)
+            {
+                ResourceRecord *nsec3RR = rr->resrec.AnonInfo->nsec3RR;
+
+                newptr = PutRR_OS_TTL(newptr, &m->omsg.h.numAuthorities, nsec3RR,  nsec3RR->rroriginalttl);
+                if (newptr)
+                {
+                    responseptr = newptr;
+                    debugf("SendResponses: Added NSEC3 %s, OwnerRecordSpace %d, TraceRecordSpace %d, limit %p", ARDisplayString(m, rr), OwnerRecordSpace,
+                            TraceRecordSpace, m->omsg.data + AllowedRRSpace(&m->omsg) - OwnerRecordSpace - TraceRecordSpace);
+                }
+                else
+                {
+                    LogMsg("SendResponses: Cannot add NSEC3 %s, OwnerRecordSpace %d, TraceRecordSpace %d, limit %p", ARDisplayString(m, rr), OwnerRecordSpace,
+                            TraceRecordSpace, m->omsg.data + AllowedRRSpace(&m->omsg) - OwnerRecordSpace - TraceRecordSpace);
+                }
+                rr->resrec.AnonInfo->SendNow = mDNSNULL;
+            }
+        }
         // Second Pass. Add additional records, if there's space.
         newptr = responseptr;
         for (rr = m->ResourceRecords; rr; rr=rr->next)
         // Second Pass. Add additional records, if there's space.
         newptr = responseptr;
         for (rr = m->ResourceRecords; rr; rr=rr->next)
@@ -2446,26 +2740,47 @@ mDNSlocal void SendResponses(mDNS *const m)
 
         if (m->omsg.h.numAnswers || m->omsg.h.numAdditionals)
         {
 
         if (m->omsg.h.numAnswers || m->omsg.h.numAdditionals)
         {
-            // If we have data to send, add OWNER option if necessary, then send packet
-
-            if (OwnerRecordSpace)
+            // If we have data to send, add OWNER/TRACER/OWNER+TRACER option if necessary, then send packet
+            if (OwnerRecordSpace || TraceRecordSpace)
             {
                 AuthRecord opt;
                 mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
                 opt.resrec.rrclass    = NormalMaxDNSMessageData;
             {
                 AuthRecord opt;
                 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.rdlength   = sizeof(rdataOPT);
                 opt.resrec.rdestimate = sizeof(rdataOPT);
                 opt.resrec.rdestimate = sizeof(rdataOPT);
-                SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]);
+                if (OwnerRecordSpace && TraceRecordSpace)
+                {
+                    opt.resrec.rdlength   += sizeof(rdataOPT); // Two options in this OPT record
+                    opt.resrec.rdestimate += sizeof(rdataOPT);
+                    SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]);
+                    SetupTracerOpt(m, &opt.resrec.rdata->u.opt[1]);
+                }
+                else if (OwnerRecordSpace)
+                {
+                    SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]);
+                }
+                else if (TraceRecordSpace)
+                {
+                    SetupTracerOpt(m, &opt.resrec.rdata->u.opt[0]);
+                }
                 newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAdditionals, &opt.resrec);
                 newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAdditionals, &opt.resrec);
-                if (newptr) { responseptr = newptr; LogSPS("SendResponses put   %s", ARDisplayString(m, &opt)); }
+                if (newptr) 
+                { 
+                    responseptr = newptr; 
+                    LogInfo("SendResponses put %s %s: %s %s", OwnerRecordSpace ? "OWNER" : "", TraceRecordSpace ? "TRACER" : "", intf->ifname, ARDisplayString(m, &opt));
+                }
                 else if (m->omsg.h.numAnswers + m->omsg.h.numAuthorities + m->omsg.h.numAdditionals == 1)
                 else if (m->omsg.h.numAnswers + m->omsg.h.numAuthorities + m->omsg.h.numAdditionals == 1)
-                    LogSPS("SendResponses: No space in packet for Owner OPT record (%d/%d/%d/%d) %s",
-                           m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt));
+                {
+                    LogInfo("SendResponses: No space in packet for %s %s OPT record (%d/%d/%d/%d) %s", OwnerRecordSpace ? "OWNER" : "", TraceRecordSpace ? "TRACER" : "",
+                            m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt));
+                }
                 else
                 else
-                    LogMsg("SendResponses: How did we fail to have space for Owner OPT record (%d/%d/%d/%d) %s",
+                {
+                    LogMsg("SendResponses: How did we fail to have space for %s %s OPT record (%d/%d/%d/%d) %s", OwnerRecordSpace ? "OWNER" : "", TraceRecordSpace ? "TRACER" : "",
                            m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt));
                            m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt));
+                }
             }
             }
-
+    
             debugf("SendResponses: Sending %d Deregistration%s, %d Announcement%s, %d Answer%s, %d Additional%s on %p",
                    numDereg,                 numDereg                 == 1 ? "" : "s",
                    numAnnounce,              numAnnounce              == 1 ? "" : "s",
             debugf("SendResponses: Sending %d Deregistration%s, %d Announcement%s, %d Answer%s, %d Additional%s on %p",
                    numDereg,                 numDereg                 == 1 ? "" : "s",
                    numAnnounce,              numAnnounce              == 1 ? "" : "s",
@@ -2576,16 +2891,15 @@ mDNSexport void SetNextCacheCheckTimeForRecord(mDNS *const m, CacheRecord *const
         verbosedebugf("SetNextCacheCheckTimeForRecord: NextRequiredQuery in %ld sec CacheCheckGracePeriod %d ticks for %s",
                       (rr->NextRequiredQuery - m->timenow) / mDNSPlatformOneSecond, CacheCheckGracePeriod(rr), CRDisplayString(m,rr));
     }
         verbosedebugf("SetNextCacheCheckTimeForRecord: NextRequiredQuery in %ld sec CacheCheckGracePeriod %d ticks for %s",
                       (rr->NextRequiredQuery - m->timenow) / mDNSPlatformOneSecond, CacheCheckGracePeriod(rr), CRDisplayString(m,rr));
     }
-
     ScheduleNextCacheCheckTime(m, HashSlot(rr->resrec.name), NextCacheCheckEvent(rr));
 }
 
 #define kMinimumReconfirmTime                     ((mDNSu32)mDNSPlatformOneSecond *  5)
 #define kDefaultReconfirmTimeForWake              ((mDNSu32)mDNSPlatformOneSecond *  5)
 #define kDefaultReconfirmTimeForNoAnswer          ((mDNSu32)mDNSPlatformOneSecond *  5)
     ScheduleNextCacheCheckTime(m, HashSlot(rr->resrec.name), NextCacheCheckEvent(rr));
 }
 
 #define kMinimumReconfirmTime                     ((mDNSu32)mDNSPlatformOneSecond *  5)
 #define kDefaultReconfirmTimeForWake              ((mDNSu32)mDNSPlatformOneSecond *  5)
 #define kDefaultReconfirmTimeForNoAnswer          ((mDNSu32)mDNSPlatformOneSecond *  5)
-#define kDefaultReconfirmTimeForFlappingInterface ((mDNSu32)mDNSPlatformOneSecond * 30)
+#define kDefaultReconfirmTimeForFlappingInterface ((mDNSu32)mDNSPlatformOneSecond *  5)
 
 
-mDNSlocal mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, mDNSu32 interval)
+mDNSexport mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, mDNSu32 interval)
 {
     if (interval < kMinimumReconfirmTime)
         interval = kMinimumReconfirmTime;
 {
     if (interval < kMinimumReconfirmTime)
         interval = kMinimumReconfirmTime;
@@ -2609,8 +2923,6 @@ mDNSlocal mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr,
     return(mStatus_NoError);
 }
 
     return(mStatus_NoError);
 }
 
-#define MaxQuestionInterval         (3600 * mDNSPlatformOneSecond)
-
 // BuildQuestion puts a question into a DNS Query packet and if successful, updates the value of queryptr.
 // It also appends to the list of known answer records that need to be included,
 // and updates the forcast for the size of the known answer section.
 // BuildQuestion puts a question into a DNS Query packet and if successful, updates the value of queryptr.
 // It also appends to the list of known answer records that need to be included,
 // and updates the forcast for the size of the known answer section.
@@ -2620,7 +2932,8 @@ mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **quer
     mDNSBool ucast = (q->LargeAnswers || q->RequestUnicast) && m->CanReceiveUnicastOn5353;
     mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0);
     const mDNSu8 *const limit = query->data + NormalMaxDNSMessageData;
     mDNSBool ucast = (q->LargeAnswers || q->RequestUnicast) && m->CanReceiveUnicastOn5353;
     mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0);
     const mDNSu8 *const limit = query->data + NormalMaxDNSMessageData;
-    mDNSu8 *newptr = putQuestion(query, *queryptr, limit - *answerforecast, &q->qname, q->qtype, (mDNSu16)(q->qclass | ucbit));
+    mDNSu8 anoninfo_space = q->AnonInfo ? AnonInfoSpace(q->AnonInfo) : 0;
+    mDNSu8 *newptr = putQuestion(query, *queryptr, limit - *answerforecast - anoninfo_space, &q->qname, q->qtype, (mDNSu16)(q->qclass | ucbit));
     if (!newptr)
     {
         debugf("BuildQuestion: No more space in this packet for question %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
     if (!newptr)
     {
         debugf("BuildQuestion: No more space in this packet for question %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
@@ -2628,7 +2941,7 @@ mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **quer
     }
     else
     {
     }
     else
     {
-        mDNSu32 forecast = *answerforecast;
+        mDNSu32 forecast = *answerforecast + anoninfo_space;
         const mDNSu32 slot = HashSlot(&q->qname);
         const CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
         CacheRecord *rr;
         const mDNSu32 slot = HashSlot(&q->qname);
         const CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
         CacheRecord *rr;
@@ -2657,9 +2970,9 @@ mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **quer
                 // then undo that last question and try again next time
                 if (query->h.numQuestions > 1 && newptr + forecast >= limit)
                 {
                 // then undo that last question and try again next time
                 if (query->h.numQuestions > 1 && newptr + forecast >= limit)
                 {
-                    debugf("BuildQuestion: Retracting question %##s (%s) new forecast total %d",
-                           q->qname.c, DNSTypeName(q->qtype), newptr + forecast - query->data);
                     query->h.numQuestions--;
                     query->h.numQuestions--;
+                    debugf("BuildQuestion: Retracting question %##s (%s) new forecast total %d, total questions %d",
+                           q->qname.c, DNSTypeName(q->qtype), newptr + forecast - query->data, query->h.numQuestions);
                     ka = *kalistptrptr;     // Go back to where we started and retract these answer records
                     while (*ka) { CacheRecord *c = *ka; *ka = mDNSNULL; ka = &c->NextInKAList; }
                     return(mDNSfalse);      // Return false, so we'll try again in the next packet
                     ka = *kalistptrptr;     // Go back to where we started and retract these answer records
                     while (*ka) { CacheRecord *c = *ka; *ka = mDNSNULL; ka = &c->NextInKAList; }
                     return(mDNSfalse);      // Return false, so we'll try again in the next packet
@@ -2729,8 +3042,10 @@ mDNSlocal const CacheRecord *CacheHasAddressTypeForName(mDNS *const m, const dom
     return(cr);
 }
 
     return(cr);
 }
 
+
 mDNSlocal const CacheRecord *FindSPSInCache1(mDNS *const m, const DNSQuestion *const q, const CacheRecord *const c0, const CacheRecord *const c1)
 {
 mDNSlocal const CacheRecord *FindSPSInCache1(mDNS *const m, const DNSQuestion *const q, const CacheRecord *const c0, const CacheRecord *const c1)
 {
+#ifndef SPC_DISABLED
     CacheGroup *const cg = CacheGroupForName(m, HashSlot(&q->qname), q->qnamehash, &q->qname);
     const CacheRecord *cr, *bestcr = mDNSNULL;
     mDNSu32 bestmetric = 1000000;
     CacheGroup *const cg = CacheGroupForName(m, HashSlot(&q->qname), q->qnamehash, &q->qname);
     const CacheRecord *cr, *bestcr = mDNSNULL;
     mDNSu32 bestmetric = 1000000;
@@ -2744,6 +3059,14 @@ mDNSlocal const CacheRecord *FindSPSInCache1(mDNS *const m, const DNSQuestion *c
                         if (bestmetric > metric) { bestmetric = metric; bestcr = cr; }
                     }
     return(bestcr);
                         if (bestmetric > metric) { bestmetric = metric; bestcr = cr; }
                     }
     return(bestcr);
+#else // SPC_DISABLED
+       (void) m;
+       (void) q;
+       (void) c0;
+       (void) c1;
+       (void) c1;
+    return mDNSNULL;
+#endif // SPC_DISABLED
 }
 
 mDNSlocal void CheckAndSwapSPS(const CacheRecord *sps1, const CacheRecord *sps2)
 }
 
 mDNSlocal void CheckAndSwapSPS(const CacheRecord *sps1, const CacheRecord *sps2)
@@ -2876,6 +3199,7 @@ mDNSlocal void mDNSSendWakeOnResolve(mDNS *const m, DNSQuestion *q)
             EthAddr[i - 1] = 0;
             mDNSPlatformMemCopy(IPAddr, &d->c[i + 1], len - i);
             IPAddr[len - i] = 0;
             EthAddr[i - 1] = 0;
             mDNSPlatformMemCopy(IPAddr, &d->c[i + 1], len - i);
             IPAddr[len - i] = 0;
+            m->mDNSStats.WakeOnResolves++;
             mDNSPlatformSendWakeupPacket(m, InterfaceID, EthAddr, IPAddr, InitialWakeOnResolveCount - q->WakeOnResolveCount);
             return;
         }
             mDNSPlatformSendWakeupPacket(m, InterfaceID, EthAddr, IPAddr, InitialWakeOnResolveCount - q->WakeOnResolveCount);
             return;
         }
@@ -2964,6 +3288,7 @@ mDNSlocal void SendQueries(mDNS *const m)
                 {
                     q->LastQTime = m->timenow - q->ThisQInterval;
                     cr->UnansweredQueries++;
                 {
                     q->LastQTime = m->timenow - q->ThisQInterval;
                     cr->UnansweredQueries++;
+                    m->mDNSStats.CacheRefreshQueries++;
                 }
                 else if (q->SendQNow == mDNSNULL)
                 {
                 }
                 else if (q->SendQNow == mDNSNULL)
                 {
@@ -2973,6 +3298,13 @@ mDNSlocal void SendQueries(mDNS *const m)
                 {
                     q->SendQNow = mDNSInterfaceMark;
                 }
                 {
                     q->SendQNow = mDNSInterfaceMark;
                 }
+
+                // Indicate that this question was marked for sending
+                // to update an existing cached answer record.
+                // The browse throttling logic below uses this to determine
+                // if the query should be sent.
+                if (mDNSOpaque16IsZero(q->TargetQID))
+                    q->CachedAnswerNeedsUpdate = mDNStrue;
             }
         }
     }
             }
         }
     }
@@ -2998,7 +3330,7 @@ mDNSlocal void SendQueries(mDNS *const m)
             {
                 InitializeDNSMessage(&m->omsg.h, q->TargetQID, QueryFlags);
                 qptr = putQuestion(&m->omsg, qptr, limit, &q->qname, q->qtype, q->qclass);
             {
                 InitializeDNSMessage(&m->omsg.h, q->TargetQID, QueryFlags);
                 qptr = putQuestion(&m->omsg, qptr, limit, &q->qname, q->qtype, q->qclass);
-                mDNSSendDNSMessage(m, &m->omsg, qptr, mDNSInterface_Any, q->LocalSocket, &q->Target, q->TargetPort, mDNSNULL, mDNSNULL, q->UseBrackgroundTrafficClass);
+                mDNSSendDNSMessage(m, &m->omsg, qptr, mDNSInterface_Any, q->LocalSocket, &q->Target, q->TargetPort, mDNSNULL, mDNSNULL, q->UseBackgroundTrafficClass);
                 q->ThisQInterval    *= QuestionIntervalStep;
             }
             if (q->ThisQInterval > MaxQuestionInterval)
                 q->ThisQInterval    *= QuestionIntervalStep;
             }
             if (q->ThisQInterval > MaxQuestionInterval)
@@ -3037,21 +3369,44 @@ mDNSlocal void SendQueries(mDNS *const m)
     m->NextScheduledQuery = m->timenow + 0x78000000;
     for (q = m->Questions; q && q != m->NewQuestions; q=q->next)
     {
     m->NextScheduledQuery = m->timenow + 0x78000000;
     for (q = m->Questions; q && q != m->NewQuestions; q=q->next)
     {
-        if (mDNSOpaque16IsZero(q->TargetQID) && (q->SendQNow ||
-                                                 (!q->Target.type && ActiveQuestion(q) && q->ThisQInterval <= maxExistingQuestionInterval && AccelerateThisQuery(m,q))))
+        if (mDNSOpaque16IsZero(q->TargetQID)
+            && (q->SendQNow || (!q->Target.type && ActiveQuestion(q) && q->ThisQInterval <= maxExistingQuestionInterval && AccelerateThisQuery(m,q))))
         {
             // If at least halfway to next query time, advance to next interval
             // If less than halfway to next query time, then
             // treat this as logically a repeat of the last transmission, without advancing the interval
             if (m->timenow - (q->LastQTime + (q->ThisQInterval/2)) >= 0)
             {
         {
             // If at least halfway to next query time, advance to next interval
             // If less than halfway to next query time, then
             // treat this as logically a repeat of the last transmission, without advancing the interval
             if (m->timenow - (q->LastQTime + (q->ThisQInterval/2)) >= 0)
             {
-                //LogInfo("Accelerating %##s (%s) %d", q->qname.c, DNSTypeName(q->qtype), m->timenow - NextQSendTime(q));
-                q->SendQNow = mDNSInterfaceMark;    // Mark this question for sending on all interfaces
+                // If we have reached the answer threshold for this question, 
+                // don't send it again until MaxQuestionInterval unless:
+                //  one of its cached answers needs to be refreshed,
+                //  or it's the initial query for a kDNSServiceFlagsThresholdFinder mode browse.
+                if (q->BrowseThreshold 
+                    && (q->CurrentAnswers >= q->BrowseThreshold)
+                    && (q->CachedAnswerNeedsUpdate == mDNSfalse)
+                    && !((q->flags & kDNSServiceFlagsThresholdFinder) && (q->ThisQInterval == InitialQuestionInterval)))
+                {
+                    q->SendQNow = mDNSNULL;
+                    q->ThisQInterval = MaxQuestionInterval;
+                    q->LastQTime = m->timenow;
+                    q->RequestUnicast = 0;
+                    LogInfo("SendQueries: (%s) %##s reached threshold of %d answers",
+                         DNSTypeName(q->qtype), q->qname.c, q->BrowseThreshold);
+                }
+                else
+                {
+                    // Mark this question for sending on all interfaces
+                    q->SendQNow = mDNSInterfaceMark;
+                    q->ThisQInterval *= QuestionIntervalStep;
+                }
+
                 debugf("SendQueries: %##s (%s) next interval %d seconds RequestUnicast = %d",
                        q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval / InitialQuestionInterval, q->RequestUnicast);
                 debugf("SendQueries: %##s (%s) next interval %d seconds RequestUnicast = %d",
                        q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval / InitialQuestionInterval, q->RequestUnicast);
-                q->ThisQInterval *= QuestionIntervalStep;
-                if (q->ThisQInterval > MaxQuestionInterval)
+
+                if (q->ThisQInterval >= QuestionIntervalThreshold)
+                {
                     q->ThisQInterval = MaxQuestionInterval;
                     q->ThisQInterval = MaxQuestionInterval;
+                }
                 else if (q->CurrentAnswers == 0 && q->ThisQInterval == InitialQuestionInterval * QuestionIntervalStep3 && !q->RequestUnicast &&
                          !(RRTypeIsAddressType(q->qtype) && CacheHasAddressTypeForName(m, &q->qname, q->qnamehash)))
                 {
                 else if (q->CurrentAnswers == 0 && q->ThisQInterval == InitialQuestionInterval * QuestionIntervalStep3 && !q->RequestUnicast &&
                          !(RRTypeIsAddressType(q->qtype) && CacheHasAddressTypeForName(m, &q->qname, q->qnamehash)))
                 {
@@ -3176,7 +3531,8 @@ mDNSlocal void SendQueries(mDNS *const m)
     // go through our interface list sending the appropriate queries on each interface
     while (intf)
     {
     // go through our interface list sending the appropriate queries on each interface
     while (intf)
     {
-        const int OwnerRecordSpace = (m->AnnounceOwner && intf->MAC.l[0]) ? DNSOpt_Header_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC) : 0;
+        int OwnerRecordSpace = (m->AnnounceOwner && intf->MAC.l[0]) ? DNSOpt_Header_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC) : 0;
+        int TraceRecordSpace = (mDNS_McastTracingEnabled && MDNS_TRACER) ? DNSOpt_Header_Space + DNSOpt_TraceData_Space : 0;
         mDNSu8 *queryptr = m->omsg.data;
         mDNSBool useBackgroundTrafficClass = mDNSfalse;    // set if we should use background traffic class
 
         mDNSu8 *queryptr = m->omsg.data;
         mDNSBool useBackgroundTrafficClass = mDNSfalse;    // set if we should use background traffic class
 
@@ -3186,13 +3542,14 @@ mDNSlocal void SendQueries(mDNS *const m)
         {
             // Start a new known-answer list
             CacheRecord **kalistptr = &KnownAnswerList;
         {
             // Start a new known-answer list
             CacheRecord **kalistptr = &KnownAnswerList;
-            mDNSu32 answerforecast = OwnerRecordSpace;      // We start by assuming we'll need at least enough space to put the Owner Option
+            mDNSu32 answerforecast = OwnerRecordSpace + TraceRecordSpace;  // Start by assuming we'll need at least enough space to put the Owner+Tracer Option
 
             // Put query questions in this packet
             for (q = m->Questions; q && q != m->NewQuestions; q=q->next)
             {
                 if (mDNSOpaque16IsZero(q->TargetQID) && (q->SendQNow == intf->InterfaceID))
                 {
 
             // Put query questions in this packet
             for (q = m->Questions; q && q != m->NewQuestions; q=q->next)
             {
                 if (mDNSOpaque16IsZero(q->TargetQID) && (q->SendQNow == intf->InterfaceID))
                 {
+                    mDNSBool Suppress = mDNSfalse;
                     debugf("SendQueries: %s question for %##s (%s) at %d forecast total %d",
                            SuppressOnThisInterface(q->DupSuppress, intf) ? "Suppressing" : "Putting    ",
                            q->qname.c, DNSTypeName(q->qtype), queryptr - m->omsg.data, queryptr + answerforecast - m->omsg.data);
                     debugf("SendQueries: %s question for %##s (%s) at %d forecast total %d",
                            SuppressOnThisInterface(q->DupSuppress, intf) ? "Suppressing" : "Putting    ",
                            q->qname.c, DNSTypeName(q->qtype), queryptr - m->omsg.data, queryptr + answerforecast - m->omsg.data);
@@ -3204,9 +3561,24 @@ mDNSlocal void SendQueries(mDNS *const m)
                         q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf);
                     }
                     // If we're suppressing this question, or we successfully put it, update its SendQNow state
                         q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf);
                     }
                     // If we're suppressing this question, or we successfully put it, update its SendQNow state
-                    else if (SuppressOnThisInterface(q->DupSuppress, intf) ||
+                    else if ((Suppress = SuppressOnThisInterface(q->DupSuppress, intf)) ||
                         BuildQuestion(m, &m->omsg, &queryptr, q, &kalistptr, &answerforecast))
                     {
                         BuildQuestion(m, &m->omsg, &queryptr, q, &kalistptr, &answerforecast))
                     {
+                        // We successfully added the question to the packet. Make sure that
+                        // we also send the NSEC3 record if required. BuildQuestion accounted for
+                        // the space.
+                        //
+                        // Note: We don't suppress anonymous questions and hence Suppress should always
+                        // be zero.
+
+                        if (Suppress)
+                            m->mDNSStats.DupQuerySuppressions++;
+
+                        if (!Suppress && q->AnonInfo)
+                        {
+                            debugf("SendQueries: marking for question %##s, Suppress %d", q->qname.c, Suppress);
+                            q->AnonInfo->SendNow = intf->InterfaceID;
+                        }
                         q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf);
                         if (q->WakeOnResolveCount)
                         {
                         q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf);
                         if (q->WakeOnResolveCount)
                         {
@@ -3215,7 +3587,7 @@ mDNSlocal void SendQueries(mDNS *const m)
                         }
 
                         // use brackground traffic class if any included question requires it
                         }
 
                         // use brackground traffic class if any included question requires it
-                        if (q->UseBrackgroundTrafficClass)
+                        if (q->UseBackgroundTrafficClass)
                         {
                             useBackgroundTrafficClass = mDNStrue;
                         }
                         {
                             useBackgroundTrafficClass = mDNStrue;
                         }
@@ -3250,8 +3622,8 @@ mDNSlocal void SendQueries(mDNS *const m)
         {
             CacheRecord *ka = KnownAnswerList;
             mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - ka->TimeRcvd)) / mDNSPlatformOneSecond;
         {
             CacheRecord *ka = KnownAnswerList;
             mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - ka->TimeRcvd)) / mDNSPlatformOneSecond;
-            mDNSu8 *newptr = PutResourceRecordTTLWithLimit(&m->omsg, queryptr, &m->omsg.h.numAnswers,
-                                                           &ka->resrec, ka->resrec.rroriginalttl - SecsSinceRcvd, m->omsg.data + NormalMaxDNSMessageData - OwnerRecordSpace);
+            mDNSu8 *newptr = PutResourceRecordTTLWithLimit(&m->omsg, queryptr, &m->omsg.h.numAnswers, &ka->resrec, ka->resrec.rroriginalttl - SecsSinceRcvd,
+                                                           m->omsg.data + NormalMaxDNSMessageData - OwnerRecordSpace - TraceRecordSpace);
             if (newptr)
             {
                 verbosedebugf("SendQueries:   Put %##s (%s) at %d - %d",
             if (newptr)
             {
                 verbosedebugf("SendQueries:   Put %##s (%s) at %d - %d",
@@ -3272,6 +3644,7 @@ mDNSlocal void SendQueries(mDNS *const m)
         }
 
         for (ar = m->ResourceRecords; ar; ar=ar->next)
         }
 
         for (ar = m->ResourceRecords; ar; ar=ar->next)
+        {
             if (ar->IncludeInProbe)
             {
                 mDNSu8 *newptr = PutResourceRecord(&m->omsg, queryptr, &m->omsg.h.numAuthorities, &ar->resrec);
             if (ar->IncludeInProbe)
             {
                 mDNSu8 *newptr = PutResourceRecord(&m->omsg, queryptr, &m->omsg.h.numAuthorities, &ar->resrec);
@@ -3279,28 +3652,66 @@ mDNSlocal void SendQueries(mDNS *const m)
                 if (newptr) queryptr = newptr;
                 else LogMsg("SendQueries:   How did we fail to have space for the Update record %s", ARDisplayString(m,ar));
             }
                 if (newptr) queryptr = newptr;
                 else LogMsg("SendQueries:   How did we fail to have space for the Update record %s", ARDisplayString(m,ar));
             }
+        }
+
+        for (q = m->Questions; q; q = q->next)
+        {
+            if (q->AnonInfo && q->AnonInfo->SendNow == intf->InterfaceID)
+            {
+                mDNSu8 *newptr = PutResourceRecord(&m->omsg, queryptr, &m->omsg.h.numAuthorities, q->AnonInfo->nsec3RR);
+                if (newptr)
+                {
+                    debugf("SendQueries: Added NSEC3 record %s on InterfaceID %p", RRDisplayString(m, q->AnonInfo->nsec3RR), intf->InterfaceID);
+                    queryptr = newptr;
+                }
+                else
+                {
+                    LogMsg("SendQueries: ERROR!! Cannot add NSEC3 record %s on InterfaceID %p", RRDisplayString(m, q->AnonInfo->nsec3RR), intf->InterfaceID);
+                }
+                q->AnonInfo->SendNow = mDNSNULL;
+            }
+        }
 
         if (queryptr > m->omsg.data)
         {
 
         if (queryptr > m->omsg.data)
         {
-            if (OwnerRecordSpace)
+            // If we have data to send, add OWNER/TRACER/OWNER+TRACER option if necessary, then send packet
+            if (OwnerRecordSpace || TraceRecordSpace)
             {
                 AuthRecord opt;
                 mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
                 opt.resrec.rrclass    = NormalMaxDNSMessageData;
             {
                 AuthRecord opt;
                 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.rdlength   = sizeof(rdataOPT); 
                 opt.resrec.rdestimate = sizeof(rdataOPT);
                 opt.resrec.rdestimate = sizeof(rdataOPT);
-                SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]);
-                LogSPS("SendQueries putting %s", ARDisplayString(m, &opt));
+                if (OwnerRecordSpace && TraceRecordSpace)
+                {
+                    opt.resrec.rdlength   += sizeof(rdataOPT);  // Two options in this OPT record
+                    opt.resrec.rdestimate += sizeof(rdataOPT);
+                    SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]);
+                    SetupTracerOpt(m, &opt.resrec.rdata->u.opt[1]);
+                }
+                else if (OwnerRecordSpace)
+                {
+                    SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]);
+                }
+                else if (TraceRecordSpace)
+                {
+                    SetupTracerOpt(m, &opt.resrec.rdata->u.opt[0]);
+                }
+                LogInfo("SendQueries putting %s %s: %s %s", OwnerRecordSpace ? "OWNER" : "", TraceRecordSpace ? "TRACER" : "", intf->ifname, ARDisplayString(m, &opt));
                 queryptr = PutResourceRecordTTLWithLimit(&m->omsg, queryptr, &m->omsg.h.numAdditionals,
                                                          &opt.resrec, opt.resrec.rroriginalttl, m->omsg.data + AbsoluteMaxDNSMessageData);
                 if (!queryptr)
                 queryptr = PutResourceRecordTTLWithLimit(&m->omsg, queryptr, &m->omsg.h.numAdditionals,
                                                          &opt.resrec, opt.resrec.rroriginalttl, m->omsg.data + AbsoluteMaxDNSMessageData);
                 if (!queryptr)
-                    LogMsg("SendQueries: How did we fail to have space for the OPT record (%d/%d/%d/%d) %s",
+                { 
+                    LogMsg("SendQueries: How did we fail to have space for %s %s OPT record (%d/%d/%d/%d) %s", OwnerRecordSpace ? "OWNER" : "", TraceRecordSpace ? "TRACER" : "",
                            m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt));
                            m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt));
+                }
                 if (queryptr > m->omsg.data + NormalMaxDNSMessageData)
                 if (queryptr > m->omsg.data + NormalMaxDNSMessageData)
+                {
                     if (m->omsg.h.numQuestions != 1 || m->omsg.h.numAnswers != 0 || m->omsg.h.numAuthorities != 1 || m->omsg.h.numAdditionals != 1)
                     if (m->omsg.h.numQuestions != 1 || m->omsg.h.numAnswers != 0 || m->omsg.h.numAuthorities != 1 || m->omsg.h.numAdditionals != 1)
-                        LogMsg("SendQueries: Why did we generate oversized packet with OPT record %p %p %p (%d/%d/%d/%d) %s",
-                               m->omsg.data, m->omsg.data + NormalMaxDNSMessageData, queryptr,
-                               m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt));
+                        LogMsg("SendQueries: Why did we generate oversized packet with %s %s OPT record %p %p %p (%d/%d/%d/%d) %s", OwnerRecordSpace ? "OWNER" : "", 
+                                TraceRecordSpace ? "TRACER" : "", m->omsg.data, m->omsg.data + NormalMaxDNSMessageData, queryptr, m->omsg.h.numQuestions, m->omsg.h.numAnswers, 
+                                m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt));
+                }
             }
 
             if ((m->omsg.h.flags.b[0] & kDNSFlag0_TC) && m->omsg.h.numQuestions > 1)
             }
 
             if ((m->omsg.h.flags.b[0] & kDNSFlag0_TC) && m->omsg.h.numQuestions > 1)
@@ -3361,6 +3772,7 @@ mDNSlocal void SendQueries(mDNS *const m)
     // Do this AFTER the lingering cache records check above, because that will prevent spurious warnings for questions
     // we legitimately couldn't send because the interface is no longer available
     for (q = m->Questions; q; q=q->next)
     // Do this AFTER the lingering cache records check above, because that will prevent spurious warnings for questions
     // we legitimately couldn't send because the interface is no longer available
     for (q = m->Questions; q; q=q->next)
+    {
         if (q->SendQNow)
         {
             DNSQuestion *x;
         if (q->SendQNow)
         {
             DNSQuestion *x;
@@ -3368,6 +3780,8 @@ mDNSlocal void SendQueries(mDNS *const m)
             LogMsg("SendQueries: No active interface %p to send %s question: %p %##s (%s)", q->SendQNow, x ? "new" : "old", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype));
             q->SendQNow = mDNSNULL;
         }
             LogMsg("SendQueries: No active interface %p to send %s question: %p %##s (%s)", q->SendQNow, x ? "new" : "old", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype));
             q->SendQNow = mDNSNULL;
         }
+        q->CachedAnswerNeedsUpdate = mDNSfalse;
+    }
 }
 
 mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *EthAddr, mDNSOpaque48 *password)
 }
 
 mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *EthAddr, mDNSOpaque48 *password)
@@ -3422,7 +3836,7 @@ mDNSlocal void ResetQuestionState(mDNS *const m, DNSQuestion *q)
     q->LastQTxTime      = m->timenow;
     q->RecentAnswerPkts = 0;
     q->ThisQInterval    = MaxQuestionInterval;
     q->LastQTxTime      = m->timenow;
     q->RecentAnswerPkts = 0;
     q->ThisQInterval    = MaxQuestionInterval;
-    q->RequestUnicast   = mDNSfalse;
+    q->RequestUnicast   = 0;
     // Reset unansweredQueries so that we don't penalize this server later when we
     // start sending queries when the cache expires.
     q->unansweredQueries = 0;
     // Reset unansweredQueries so that we don't penalize this server later when we
     // start sending queries when the cache expires.
     q->unansweredQueries = 0;
@@ -3445,14 +3859,16 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco
     // a RMV for a single record in the rrset, we invalidate the response. If we deliver another add
     // in the future, we will do the revalidation again.
     //
     // a RMV for a single record in the rrset, we invalidate the response. If we deliver another add
     // in the future, we will do the revalidation again.
     //
-    // Also, if we deliver an ADD for a negative cache record and it has no NSECS, the ValidationStatus needs
+    // Also, if we deliver an ADD for a negative cache record and it has no NSEC/NSEC3, the ValidationStatus needs
     // to be reset. This happens normally when we deliver a "secure" negative response followed by an insecure
     // to be reset. This happens normally when we deliver a "secure" negative response followed by an insecure
-    // negative response which can happen e.g., when disconnecting from network. As we don't deliver RMVs for
-    // negative responses that were delivered before, we need to do it on the next ADD of a negative cache
-    // record. This ADD could be the result of a timeout, no DNS servers etc. If we don't reset the state, we
-    // will deliver this as a secure response.
+    // negative response which can happen e.g., when disconnecting from network that leads to a negative response
+    // due to no DNS servers. As we don't deliver RMVs for negative responses that were delivered before, we need
+    // to do it on the next ADD of a negative cache record. This ADD could be the result of a timeout, no DNS servers
+    // etc. in which case we need to reset the state to make sure we don't deliver them as secure. If this is
+    // a real negative response, we would reset the state here and validate the results at the end of this function.
+    // or the real response again if we purge the cache. 
     if (q->ValidationRequired && ((AddRecord == QC_rmv) ||
     if (q->ValidationRequired && ((AddRecord == QC_rmv) ||
-        (rr->resrec.RecordType == kDNSRecordTypePacketNegative && !rr->nsec)))
+        (rr->resrec.RecordType == kDNSRecordTypePacketNegative && (AddRecord == QC_add))))
     {
         q->ValidationStatus = 0;
         q->ValidationState = DNSSECValRequired;
     {
         q->ValidationStatus = 0;
         q->ValidationState = DNSSECValRequired;
@@ -3474,7 +3890,7 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco
     {
         // 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
     {
         // 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.
+        // 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;
     }
         if (!q->TimeoutQuestion || rr->resrec.RecordType != kDNSRecordTypePacketNegative || (m->timenow - q->StopTime < 0))
             return;
     }
@@ -3510,9 +3926,9 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco
     if (rr->DelayDelivery) return;      // We'll come back later when CacheRecordDeferredAdd() calls us
 
     // Only deliver negative answers if client has explicitly requested them except when we are forcing a negative response
     if (rr->DelayDelivery) return;      // We'll come back later when CacheRecordDeferredAdd() calls us
 
     // Only deliver negative answers if client has explicitly requested them except when we are forcing a negative response
-    // for the purpose of retrying search domains
+    // for the purpose of retrying search domains/timeout OR the question is suppressed
     if (rr->resrec.RecordType == kDNSRecordTypePacketNegative || (q->qtype != kDNSType_NSEC && RRAssertsNonexistence(&rr->resrec, q->qtype)))
     if (rr->resrec.RecordType == kDNSRecordTypePacketNegative || (q->qtype != kDNSType_NSEC && RRAssertsNonexistence(&rr->resrec, q->qtype)))
-        if (!AddRecord || (AddRecord != QC_forceresponse && !q->ReturnIntermed)) return;
+        if (!AddRecord || (AddRecord != QC_suppressed && AddRecord != QC_forceresponse && !q->ReturnIntermed)) return;
 
     // For CNAME results to non-CNAME questions, only inform the client if they explicitly requested that
     if (q->QuestionCallback && !q->NoAnswer && (!followcname || q->ReturnIntermed))
 
     // For CNAME results to non-CNAME questions, only inform the client if they explicitly requested that
     if (q->QuestionCallback && !q->NoAnswer && (!followcname || q->ReturnIntermed))
@@ -3537,18 +3953,12 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco
     // 3) The name does not exist
     // 4) No DNS servers are available and we need a quick response for the application
     //
     // 3) The name does not exist
     // 4) No DNS servers are available and we need a quick response for the application
     //
-    // (1) and (2) are handled by "QC_add" check as AddRecord would be "QC_forceresponse" in that case.
-    // For (3), it is possible that we don't get nsecs back but we still need to call VerifySignature so
-    // that we can deliver the appropriate DNSSEC result. There is no point in verifying signature for (4)
-    // and hence the explicit check for q->qDNSServer.
+    // (1) and (2) are handled by "QC_add" check as AddRecord would be "QC_forceresponse" or "QC_suppressed"
+    // in that case. For (3), it is possible that we don't get nsecs back but we still need to call
+    // VerifySignature so that we can deliver the appropriate DNSSEC result. There is no point in verifying
+    // signature for (4) and hence the explicit check for q->qDNSServer.
     //
     //
-    // Note: It is important that we avoid (4) here because once the state is set to DNSSECValInProgress,
-    // we won't verify the signature when it is really needed. For example, start a query (which is answered
-    // securely), disconnect from the network. (3) would happen now and if we start the verification, we 
-    // move DNSSECValInProgress but never have a chance to go back to DNSSECValRequired as we don't deliver
-    // RMVs for negative response that was added before.
-    //
-    if (m->CurrentQuestion == q && (AddRecord == QC_add) && !q->ValidatingResponse &&
+    if (m->CurrentQuestion == q && (AddRecord == QC_add) && !q->ValidatingResponse && q->ValidationRequired &&
         q->ValidationState == DNSSECValRequired && q->qDNSServer)
     {
         q->ValidationState = DNSSECValInProgress;
         q->ValidationState == DNSSECValRequired && q->qDNSServer)
     {
         q->ValidationState = DNSSECValInProgress;
@@ -3565,7 +3975,7 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco
     // If we get a CNAME back while we are validating the response (i.e., CNAME for DS, DNSKEY, RRSIG),
     // don't follow them. If it is a ValidationRequired question, wait for the CNAME to be validated
     // first before following it
     // If we get a CNAME back while we are validating the response (i.e., CNAME for DS, DNSKEY, RRSIG),
     // don't follow them. If it is a ValidationRequired question, wait for the CNAME to be validated
     // first before following it
-    if (!DNSSECQuestion(q) && followcname && m->CurrentQuestion == q)
+    if (!ValidatingQuestion(q) && followcname && m->CurrentQuestion == q)
         AnswerQuestionByFollowingCNAME(m, q, &rr->resrec);
 }
 
         AnswerQuestionByFollowingCNAME(m, q, &rr->resrec);
 }
 
@@ -3587,19 +3997,65 @@ mDNSlocal void CacheRecordDeferredAdd(mDNS *const m, CacheRecord *rr)
     m->CurrentQuestion = mDNSNULL;
 }
 
     m->CurrentQuestion = mDNSNULL;
 }
 
-mDNSlocal mDNSs32 CheckForSoonToExpireRecords(mDNS *const m, const domainname *const name, const mDNSu32 namehash, const mDNSu32 slot)
+mDNSlocal mDNSs32 CheckForSoonToExpireRecords(mDNS *const m, const domainname *const name, const mDNSu32 namehash, const mDNSu32 slot, mDNSBool *purge)
 {
     const mDNSs32 threshhold = m->timenow + mDNSPlatformOneSecond;  // See if there are any records expiring within one second
     const mDNSs32 start      = m->timenow - 0x10000000;
     mDNSs32 delay = start;
     CacheGroup *cg = CacheGroupForName(m, slot, namehash, name);
     const CacheRecord *rr;
 {
     const mDNSs32 threshhold = m->timenow + mDNSPlatformOneSecond;  // See if there are any records expiring within one second
     const mDNSs32 start      = m->timenow - 0x10000000;
     mDNSs32 delay = start;
     CacheGroup *cg = CacheGroupForName(m, slot, namehash, name);
     const CacheRecord *rr;
+
+    if (purge)
+        *purge = mDNSfalse;
     for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
     for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
+    {
+        // If there are records that will expire soon, there are cases that need delayed
+        // delivery of events:
+        //
+        // 1) A new cache entry is about to be added as a replacement. The caller needs to
+        //    deliver a RMV (for the current old entry) followed by ADD (for the new entry).
+        //    It needs to schedule the timer for the next cache expiry (ScheduleNextCacheCheckTime),
+        //    so that the cache entry can be purged (purging causes the RMV followed by ADD)
+        //   
+        // 2) A new question is about to be answered and the caller needs to know whether it's
+        //    scheduling should be delayed so that the question is not answered with this record.
+        //    Instead of delivering an ADD (old entry) followed by RMV (old entry) and another ADD
+        //    (new entry), a single ADD can be delivered by delaying the scheduling of the question
+        //    immediately.
+        //
+        // When the unicast cache record is created, it's TTL has been extended beyond its value
+        // given in the resource record (See RRAdjustTTL). If it is in the "extended" time, the
+        // cache is already expired and we set "purge" to indicate that. When "purge" is set, the
+        // return value of the function should be ignored by the callers.
+        //
+        // Note: For case (1), "purge" argument is NULL and hence the following checks are skipped.
+        // It is okay to skip in that case because the cache records have been set to expire almost
+        // immediately and the extended time does not apply.
+        //
+        // Also, if there is already an active question we don't try to optimize as purging the cache
+        // would end up delivering RMV for the active question and hence we avoid that.
+
+        if (purge && !rr->resrec.InterfaceID && !rr->CRActiveQuestion && rr->resrec.rroriginalttl)
+        {
+            mDNSu32 uTTL = RRUnadjustedTTL(rr->resrec.rroriginalttl);
+            if (m->timenow - (rr->TimeRcvd + ((mDNSs32)uTTL * mDNSPlatformOneSecond)) >= 0)
+            {
+                LogInfo("CheckForSoonToExpireRecords: %s: rroriginalttl %u, unadjustedTTL %u, currentTTL %u",
+                    CRDisplayString(m, rr), rr->resrec.rroriginalttl, uTTL, (m->timenow - rr->TimeRcvd)/mDNSPlatformOneSecond);
+                *purge = mDNStrue;
+                continue;
+            }
+        }
         if (threshhold - RRExpireTime(rr) >= 0)     // If we have records about to expire within a second
         if (threshhold - RRExpireTime(rr) >= 0)     // If we have records about to expire within a second
+        {
             if (delay - RRExpireTime(rr) < 0)       // then delay until after they've been deleted
                 delay = RRExpireTime(rr);
             if (delay - RRExpireTime(rr) < 0)       // then delay until after they've been deleted
                 delay = RRExpireTime(rr);
-    if (delay - start > 0) return(NonZeroTime(delay));
-    else return(0);
+        }
+    }
+    if (delay - start > 0)
+        return(NonZeroTime(delay));
+    else
+        return(0);
 }
 
 // CacheRecordAdd is only called from CreateNewCacheEntry, *never* directly as a result of a client API call.
 }
 
 // CacheRecordAdd is only called from CreateNewCacheEntry, *never* directly as a result of a client API call.
@@ -3644,6 +4100,7 @@ mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr)
                           &rr->resrec.rDNSServer->addr : mDNSNULL, mDNSVal16(rr->resrec.rDNSServer ?
                                                                              rr->resrec.rDNSServer->port : zeroIPPort), q);
             q->CurrentAnswers++;
                           &rr->resrec.rDNSServer->addr : mDNSNULL, mDNSVal16(rr->resrec.rDNSServer ?
                                                                              rr->resrec.rDNSServer->port : zeroIPPort), q);
             q->CurrentAnswers++;
+
             q->unansweredQueries = 0;
             if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++;
             if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++;
             q->unansweredQueries = 0;
             if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++;
             if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++;
@@ -3748,6 +4205,17 @@ mDNSlocal void CacheRecordRmv(mDNS *const m, CacheRecord *rr)
                 if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--;
                 if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers--;
             }
                 if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--;
                 if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers--;
             }
+
+            // If we have dropped below the answer threshold for this mDNS question,
+            // restart the queries at InitialQuestionInterval.
+            if (mDNSOpaque16IsZero(q->TargetQID) && (q->BrowseThreshold > 0) && (q->CurrentAnswers < q->BrowseThreshold))
+            {
+                q->ThisQInterval = InitialQuestionInterval;
+                q->LastQTime     = m->timenow - q->ThisQInterval;
+                SetNextQueryTime(m,q);
+                LogInfo("CacheRecordRmv: (%s) %##s dropped below threshold of %d answers",
+                    DNSTypeName(q->qtype), q->qname.c, q->BrowseThreshold);
+            }
             if (rr->resrec.rdata->MaxRDLength) // Never generate "remove" events for negative results
             {
                 if (q->CurrentAnswers == 0)
             if (rr->resrec.rdata->MaxRDLength) // Never generate "remove" events for negative results
             {
                 if (q->CurrentAnswers == 0)
@@ -3790,30 +4258,8 @@ mDNSlocal void ReleaseCacheGroup(mDNS *const m, CacheGroup **cp)
     ReleaseCacheEntity(m, e);
 }
 
     ReleaseCacheEntity(m, e);
 }
 
-mDNSexport void ReleaseCacheRecord(mDNS *const m, CacheRecord *r)
+mDNSlocal void ReleaseAdditionalCacheRecords(mDNS *const m, CacheRecord **rp)
 {
 {
-    CacheGroup *cg;
-    CacheRecord **rp;
-    const mDNSu32 slot = HashSlot(r->resrec.name);
-
-    //LogMsg("ReleaseCacheRecord: Releasing %s", CRDisplayString(m, r));
-    if (r->resrec.rdata && r->resrec.rdata != (RData*)&r->smallrdatastorage) mDNSPlatformMemFree(r->resrec.rdata);
-    r->resrec.rdata = mDNSNULL;
-
-    cg = CacheGroupForRecord(m, slot, &r->resrec);
-    if (!cg) LogMsg("ReleaseCacheRecord: ERROR!! cg NULL for %##s (%s)", r->resrec.name->c, DNSTypeName(r->resrec.rrtype));
-
-    // When NSEC records are not added to the cache, it is usually cached at the "nsec" list
-    // of the CacheRecord. But sometimes they may be freed without adding to the "nsec" list
-    // (which is handled below) and in that case it should be freed here.
-    if (r->resrec.name && cg && r->resrec.name != cg->name)
-    {
-        LogInfo("ReleaseCacheRecord: freeing %##s (%s)", r->resrec.name->c, DNSTypeName(r->resrec.rrtype));
-        mDNSPlatformMemFree((void *)r->resrec.name);
-    }
-    r->resrec.name = mDNSNULL;
-
-    rp = &(r->nsec);
     while (*rp)
     {
         CacheRecord *rr = *rp;
     while (*rp)
     {
         CacheRecord *rr = *rp;
@@ -3823,20 +4269,72 @@ mDNSexport void ReleaseCacheRecord(mDNS *const m, CacheRecord *r)
             mDNSPlatformMemFree(rr->resrec.rdata);
             rr->resrec.rdata = mDNSNULL;
         }
             mDNSPlatformMemFree(rr->resrec.rdata);
             rr->resrec.rdata = mDNSNULL;
         }
-        // NSEC records that are added to the "nsec" list does not share the name
+        // NSEC or SOA records that are not added to the CacheGroup do not share the name
         // of the CacheGroup.
         if (rr->resrec.name)
         {
         // of the CacheGroup.
         if (rr->resrec.name)
         {
-            LogInfo("ReleaseCacheRecord: freeing cached nsec %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+            debugf("ReleaseAdditionalCacheRecords: freeing cached record %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
             mDNSPlatformMemFree((void *)rr->resrec.name);
             rr->resrec.name = mDNSNULL;
         }
             mDNSPlatformMemFree((void *)rr->resrec.name);
             rr->resrec.name = mDNSNULL;
         }
+        // Don't count the NSEC3 records used by anonymous browse/reg
+        if (!rr->resrec.InterfaceID)
+        {
+            m->rrcache_totalused_unicast -= rr->resrec.rdlength;
+            if (DNSSECRecordType(rr->resrec.rrtype))
+                BumpDNSSECStats(m, kStatsActionDecrement, kStatsTypeMemoryUsage, rr->resrec.rdlength);
+        }
         ReleaseCacheEntity(m, (CacheEntity *)rr);
     }
         ReleaseCacheEntity(m, (CacheEntity *)rr);
     }
-    ReleaseCacheEntity(m, (CacheEntity *)r);
 }
 
 }
 
-// Note: We want to be careful that we deliver all the CacheRecordRmv calls before delivering
+mDNSexport void ReleaseCacheRecord(mDNS *const m, CacheRecord *r)
+{
+    CacheGroup *cg;
+    const mDNSu32 slot = HashSlot(r->resrec.name);
+
+    //LogMsg("ReleaseCacheRecord: Releasing %s", CRDisplayString(m, r));
+    if (r->resrec.rdata && r->resrec.rdata != (RData*)&r->smallrdatastorage) mDNSPlatformMemFree(r->resrec.rdata);
+    r->resrec.rdata = mDNSNULL;
+
+    cg = CacheGroupForRecord(m, slot, &r->resrec);
+    
+    if (!cg)
+    {
+        // It is okay to have this printed for NSEC/NSEC3s
+        LogInfo("ReleaseCacheRecord: ERROR!! cg NULL for %##s (%s)", r->resrec.name->c, DNSTypeName(r->resrec.rrtype));
+    }
+    // When NSEC records are not added to the cache, it is usually cached at the "nsec" list
+    // of the CacheRecord. But sometimes they may be freed without adding to the "nsec" list
+    // (which is handled below) and in that case it should be freed here.
+    if (r->resrec.name && cg && r->resrec.name != cg->name)
+    {
+        debugf("ReleaseCacheRecord: freeing %##s (%s)", r->resrec.name->c, DNSTypeName(r->resrec.rrtype));
+        mDNSPlatformMemFree((void *)r->resrec.name);
+    }
+    r->resrec.name = mDNSNULL;
+
+    if (r->resrec.AnonInfo)
+    {
+        debugf("ReleaseCacheRecord: freeing AnonInfo for %##s (%s)", r->resrec.name->c, DNSTypeName(r->resrec.rrtype));
+        FreeAnonInfo((void *)r->resrec.AnonInfo);
+    }
+    r->resrec.AnonInfo = mDNSNULL;
+
+    if (!r->resrec.InterfaceID)
+    {
+        m->rrcache_totalused_unicast -= r->resrec.rdlength;
+        if (DNSSECRecordType(r->resrec.rrtype))
+            BumpDNSSECStats(m, kStatsActionDecrement, kStatsTypeMemoryUsage, r->resrec.rdlength);
+    }
+
+    ReleaseAdditionalCacheRecords(m, &r->nsec);
+    ReleaseAdditionalCacheRecords(m, &r->soa);
+
+    ReleaseCacheEntity(m, (CacheEntity *)r);
+}
+
+// Note: We want to be careful that we deliver all the CacheRecordRmv calls before delivering
 // CacheRecordDeferredAdd calls. The in-order nature of the cache lists ensures that all
 // callbacks for old records are delivered before callbacks for newer records.
 mDNSlocal void CheckCacheExpiration(mDNS *const m, const mDNSu32 slot, CacheGroup *const cg)
 // CacheRecordDeferredAdd calls. The in-order nature of the cache lists ensures that all
 // callbacks for old records are delivered before callbacks for newer records.
 mDNSlocal void CheckCacheExpiration(mDNS *const m, const mDNSu32 slot, CacheGroup *const cg)
@@ -3853,6 +4351,7 @@ mDNSlocal void CheckCacheExpiration(mDNS *const m, const mDNSu32 slot, CacheGrou
         if (m->timenow - event >= 0)    // If expired, delete it
         {
             *rp = rr->next;             // Cut it from the list
         if (m->timenow - event >= 0)    // If expired, delete it
         {
             *rp = rr->next;             // Cut it from the list
+            
             verbosedebugf("CheckCacheExpiration: Deleting%7d %7d %p %s",
                           m->timenow - rr->TimeRcvd, rr->resrec.rroriginalttl, rr->CRActiveQuestion, CRDisplayString(m, rr));
             if (rr->CRActiveQuestion)   // If this record has one or more active questions, tell them it's going away
             verbosedebugf("CheckCacheExpiration: Deleting%7d %7d %p %s",
                           m->timenow - rr->TimeRcvd, rr->resrec.rroriginalttl, rr->CRActiveQuestion, CRDisplayString(m, rr));
             if (rr->CRActiveQuestion)   // If this record has one or more active questions, tell them it's going away
@@ -3911,14 +4410,119 @@ mDNSlocal void CheckCacheExpiration(mDNS *const m, const mDNSu32 slot, CacheGrou
     m->lock_rrcache = 0;
 }
 
     m->lock_rrcache = 0;
 }
 
+// "LORecord" includes both LocalOnly and P2P record. This function assumes m->CurrentQuestion is pointing to "q".
+//
+// If "CheckOnly" is set to "true", the question won't be answered but just check to see if there is an answer and
+// returns true if there is an answer.
+//
+// If "CheckOnly" is set to "false", the question will be answered if there is a LocalOnly/P2P record and 
+// returns true to indicate the same.
+mDNSlocal mDNSBool AnswerQuestionWithLORecord(mDNS *const m, DNSQuestion *q, mDNSBool checkOnly)
+{
+    mDNSu32 slot;
+    AuthRecord *lr;
+    AuthGroup *ag;
+
+    if (m->CurrentRecord)
+        LogMsg("AnswerQuestionWithLORecord 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)
+    {
+        m->CurrentRecord = ag->members;
+        while (m->CurrentRecord && m->CurrentRecord != ag->NewLocalOnlyRecords)
+        {
+            AuthRecord *rr = m->CurrentRecord;
+            m->CurrentRecord = rr->next;
+            //
+            // 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))
+                {
+                    if (checkOnly)
+                    {
+                        LogInfo("AnswerQuestionWithLORecord: question %##s (%s) answered by %s", q->qname.c, DNSTypeName(q->qtype),
+                            ARDisplayString(m, rr));
+                        m->CurrentRecord = mDNSNULL;
+                        return mDNStrue;
+                    }
+                    AnswerLocalQuestionWithLocalAuthRecord(m, rr, mDNStrue);
+                    if (m->CurrentQuestion != q)
+                        break;     // If callback deleted q, then we're finished here
+                }
+        }
+    }
+    m->CurrentRecord = mDNSNULL;
+
+    if (m->CurrentQuestion != q)
+    {
+        LogInfo("AnswerQuestionWithLORecord: Question deleted while while answering LocalOnly record answers");
+        return mDNStrue;
+    }
+
+    if (q->LOAddressAnswers)
+    {
+        LogInfo("AnswerQuestionWithLORecord: Question %p %##s (%s) answered using local auth records LOAddressAnswers %d",
+                q, q->qname.c, DNSTypeName(q->qtype), q->LOAddressAnswers);
+        return mDNStrue;
+    }
+
+    // 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 (UniqueLocalOnlyRecord(lr) && LocalOnlyRecordAnswersQuestion(lr, q))
+            {
+                LogInfo("AnswerQuestionWithLORecord: Question %p %##s (%s) will be answered using new local auth records "
+                        " LOAddressAnswers %d", q, q->qname.c, DNSTypeName(q->qtype), q->LOAddressAnswers);
+                return mDNStrue;
+            }
+            lr = lr->next;
+        }
+    }
+    return mDNSfalse;
+}
+
+// Today, we suppress questions (not send them on the wire) for several reasons e.g.,
+// AAAA query is suppressed because no IPv6 capability or PID is not allowed to make
+// DNS requests. We need to temporarily suspend the suppress status so that we can
+// deliver a negative response (AnswerCurrentQuestionWithResourceRecord does not answer
+// suppressed questions) and reset it back. In the future, if there are other
+// reasons for suppressing the query, this function should be updated.
+mDNSlocal void AnswerSuppressedQuestion(mDNS *const m, DNSQuestion *q)
+{
+    mDNSBool SuppressQuery = q->SuppressQuery;
+    mDNSBool DisallowPID   = q->DisallowPID;
+
+    // make sure that QuerySuppressed() returns false
+    q->SuppressQuery = mDNSfalse;
+    q->DisallowPID   = mDNSfalse;
+
+    GenerateNegativeResponse(m, QC_suppressed);
+
+    q->SuppressQuery = SuppressQuery;
+    q->DisallowPID   = DisallowPID;
+}
+
 mDNSlocal void AnswerNewQuestion(mDNS *const m)
 {
     mDNSBool ShouldQueryImmediately = mDNStrue;
     DNSQuestion *const q = m->NewQuestions;     // Grab the question we're going to answer
     mDNSu32 slot = HashSlot(&q->qname);
     CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
 mDNSlocal void AnswerNewQuestion(mDNS *const m)
 {
     mDNSBool ShouldQueryImmediately = mDNStrue;
     DNSQuestion *const q = m->NewQuestions;     // Grab the question we're going to answer
     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));
     mDNSBool AnsweredFromCache = mDNSfalse;
 
     verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
@@ -3964,67 +4568,17 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m)
         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) 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; }
-
-    // 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)
-    {
-        m->CurrentRecord = ag->members;
-        while (m->CurrentRecord && m->CurrentRecord != ag->NewLocalOnlyRecords)
-        {
-            AuthRecord *rr = m->CurrentRecord;
-            m->CurrentRecord = rr->next;
-            //
-            // 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, rr, mDNStrue);
-                    if (m->CurrentQuestion != q) break;     // If callback deleted q, then we're finished here
-                }
-        }
-    }
-    m->CurrentRecord = mDNSNULL;
-
-    if (m->CurrentQuestion != q) { LogInfo("AnswerNewQuestion: Question deleted while while giving LocalOnly record answers"); goto exit; }
 
 
-    if (q->LOAddressAnswers)
+    if (m->CurrentQuestion != q)
     {
     {
-        LogInfo("AnswerNewQuestion: Question %p %##s (%s) answered using local auth records LOAddressAnswers %d",
-                q, q->qname.c, DNSTypeName(q->qtype), q->LOAddressAnswers);
+        LogInfo("AnswerNewQuestion: Question deleted while generating NoAnswer_Fail response");
         goto exit;
     }
 
         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;
-        }
-    }
-
+    // See if we want to tell it about LocalOnly/P2P records. If we answered them using LocalOnly
+    // or P2P record, then we are done.
+    if (AnswerQuestionWithLORecord(m, q, mDNSfalse))
+        goto exit;
 
     // 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 we are not supposed to answer this question, generate a negative response.
     // Temporarily suspend the SuppressQuery so that AnswerCurrentQuestionWithResourceRecord can answer the question
@@ -4032,7 +4586,10 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m)
     // If it is a question trying to validate some response, it already checked the cache for a response. If it still
     // reissues a question it means it could not find the RRSIGs. So, we need to bypass the cache check and send
     // the question out.
     // If it is a question trying to validate some response, it already checked the cache for a response. If it still
     // reissues a question it means it could not find the RRSIGs. So, we need to bypass the cache check and send
     // the question out.
-    if (QuerySuppressed(q)) { q->SuppressQuery = mDNSfalse; GenerateNegativeResponse(m); q->SuppressQuery = mDNStrue; }
+    if (QuerySuppressed(q))
+    {
+        AnswerSuppressedQuestion(m, q);
+    }
     else if (!q->ValidatingResponse)
     {
         CacheRecord *rr;
     else if (!q->ValidatingResponse)
     {
         CacheRecord *rr;
@@ -4073,7 +4630,7 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m)
     if (!QuerySuppressed(q) && !AnsweredFromCache && q->RetryWithSearchDomains)
     {
         LogInfo("AnswerNewQuestion: Generating response for retrying with search domains %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
     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);
+        GenerateNegativeResponse(m, QC_forceresponse);
     }
 
     if (m->CurrentQuestion != q) { debugf("AnswerNewQuestion: Question deleted while giving negative answer"); goto exit; }
     }
 
     if (m->CurrentQuestion != q) { debugf("AnswerNewQuestion: Question deleted while giving negative answer"); goto exit; }
@@ -4296,8 +4853,8 @@ mDNSlocal CacheGroup *GetCacheGroup(mDNS *const m, const mDNSu32 slot, const Res
 
 mDNSexport void mDNS_PurgeCacheResourceRecord(mDNS *const m, CacheRecord *rr)
 {
 
 mDNSexport void mDNS_PurgeCacheResourceRecord(mDNS *const m, CacheRecord *rr)
 {
-    if (m->mDNS_busy != m->mDNS_reentrancy+1)
-        LogMsg("mDNS_PurgeCacheResourceRecord: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+    mDNS_CheckLock(m);
+
     // Make sure we mark this record as thoroughly expired -- we don't ever want to give
     // a positive answer using an expired record (e.g. from an interface that has gone away).
     // We don't want to clear CRActiveQuestion here, because that would leave the record subject to
     // Make sure we mark this record as thoroughly expired -- we don't ever want to give
     // a positive answer using an expired record (e.g. from an interface that has gone away).
     // We don't want to clear CRActiveQuestion here, because that would leave the record subject to
@@ -4400,10 +4957,13 @@ mDNSlocal void TimeoutQuestions(mDNS *const m)
         DNSQuestion *const q = m->CurrentQuestion;
         if (q->StopTime)
         {
         DNSQuestion *const q = m->CurrentQuestion;
         if (q->StopTime)
         {
+            if (!q->TimeoutQuestion)
+                LogMsg("TimeoutQuestions: ERROR!! TimeoutQuestion not set, but StopTime set for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+
             if (m->timenow - q->StopTime >= 0)
             {
             if (m->timenow - q->StopTime >= 0)
             {
-                LogInfo("TimeoutQuestions: question %##s timed out, time %d", q->qname.c, m->timenow - q->StopTime);
-                GenerateNegativeResponse(m);
+                LogInfo("TimeoutQuestions: question %p %##s timed out, time %d", q, q->qname.c, m->timenow - q->StopTime);
+                GenerateNegativeResponse(m, QC_forceresponse);
                 if (m->CurrentQuestion == q) q->StopTime = 0;
             }
             else
                 if (m->CurrentQuestion == q) q->StopTime = 0;
             }
             else
@@ -4425,7 +4985,6 @@ mDNSlocal void mDNSCoreFreeProxyRR(mDNS *const m)
 {
     AuthRecord *rrPtr = m->SPSRRSet, *rrNext = mDNSNULL;
     LogSPS("%s : Freeing stored sleep proxy A/AAAA records", __func__);
 {
     AuthRecord *rrPtr = m->SPSRRSet, *rrNext = mDNSNULL;
     LogSPS("%s : Freeing stored sleep proxy A/AAAA records", __func__);
-    rrPtr  = m->SPSRRSet;
     while (rrPtr)
     {
         rrNext = rrPtr->next;
     while (rrPtr)
     {
         rrNext = rrPtr->next;
@@ -4439,6 +4998,10 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m)
 {
     mDNS_Lock(m);   // Must grab lock before trying to read m->timenow
 
 {
     mDNS_Lock(m);   // Must grab lock before trying to read m->timenow
 
+#if APPLE_OSX_mDNSResponder
+    mDNSLogStatistics(m);
+#endif // APPLE_OSX_mDNSResponder
+
     if (m->timenow - m->NextScheduledEvent >= 0)
     {
         int i;
     if (m->timenow - m->NextScheduledEvent >= 0)
     {
         int i;
@@ -4506,26 +5069,11 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m)
             mDNS_SendKeepalives(m);
         }
 
             mDNS_SendKeepalives(m);
         }
 
-        // After two seconds after the owner option is set, call the ioctl to clear the
-        // ignore neighbor advertisement flag.
-#if APPLE_OSX_mDNSResponder
-        if (m->clearIgnoreNA &&  m->timenow - m->clearIgnoreNA >= 0)
-        {
-            mDNSPlatformToggleInterfaceAdvt(m, mDNSfalse);
-            m->clearIgnoreNA = 0;
-        }
-#endif
         // Clear AnnounceOwner if necessary. (Do this *before* SendQueries() and SendResponses().)
         if (m->AnnounceOwner && m->timenow - m->AnnounceOwner >= 0)
         {
             m->AnnounceOwner = 0;
         }
         // Clear AnnounceOwner if necessary. (Do this *before* SendQueries() and SendResponses().)
         if (m->AnnounceOwner && m->timenow - m->AnnounceOwner >= 0)
         {
             m->AnnounceOwner = 0;
         }
-        if (m->ClearSPSRecords && m->timenow - m->ClearSPSRecords >= 0)
-        {
-            // Free the stored records that we had registered with the sleep proxy
-            mDNSCoreFreeProxyRR(m);
-            m->ClearSPSRecords = 0;
-        }
 
         if (m->DelaySleep && m->timenow - m->DelaySleep >= 0)
         {
 
         if (m->DelaySleep && m->timenow - m->DelaySleep >= 0)
         {
@@ -4717,6 +5265,7 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m)
     return(m->NextScheduledEvent);
 }
 
     return(m->NextScheduledEvent);
 }
 
+#ifndef UNICAST_DISABLED
 mDNSlocal void SuspendLLQs(mDNS *m)
 {
     DNSQuestion *q;
 mDNSlocal void SuspendLLQs(mDNS *m)
 {
     DNSQuestion *q;
@@ -4724,6 +5273,7 @@ mDNSlocal void SuspendLLQs(mDNS *m)
         if (ActiveQuestion(q) && !mDNSOpaque16IsZero(q->TargetQID) && q->LongLived && q->state == LLQ_Established)
         { q->ReqLease = 0; sendLLQRefresh(m, q); }
 }
         if (ActiveQuestion(q) && !mDNSOpaque16IsZero(q->TargetQID) && q->LongLived && q->state == LLQ_Established)
         { q->ReqLease = 0; sendLLQRefresh(m, q); }
 }
+#endif // UNICAST_DISABLED
 
 mDNSlocal mDNSBool QuestionHasLocalAnswers(mDNS *const m, DNSQuestion *q)
 {
 
 mDNSlocal mDNSBool QuestionHasLocalAnswers(mDNS *const m, DNSQuestion *q)
 {
@@ -4737,7 +5287,7 @@ mDNSlocal mDNSBool QuestionHasLocalAnswers(mDNS *const m, DNSQuestion *q)
     {
         for (rr = ag->members; rr; rr=rr->next)
             // Filter the /etc/hosts records - LocalOnly, Unique, A/AAAA/CNAME
     {
         for (rr = ag->members; rr; rr=rr->next)
             // Filter the /etc/hosts records - LocalOnly, Unique, A/AAAA/CNAME
-            if (LORecordAnswersAddressType(rr) && LocalOnlyRecordAnswersQuestion(rr, q))
+            if (UniqueLocalOnlyRecord(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;
             {
                 LogInfo("QuestionHasLocalAnswers: Question %p %##s (%s) has local answer %s", q, q->qname.c, DNSTypeName(q->qtype), ARDisplayString(m, rr));
                 return mDNStrue;
@@ -4803,7 +5353,7 @@ mDNSexport void mDNSCoreRestartAddressQueries(mDNS *const m, mDNSBool SearchDoma
     DNSQuestion *q;
     DNSQuestion *restart = mDNSNULL;
 
     DNSQuestion *q;
     DNSQuestion *restart = mDNSNULL;
 
-    if (!m->mDNS_busy) LogMsg("mDNSCoreRestartAddressQueries: ERROR!! Lock not held");
+    mDNS_CheckLock(m);
 
     // 1. Flush the cache records
     if (flushCacheRecords) flushCacheRecords(m);
 
     // 1. Flush the cache records
     if (flushCacheRecords) flushCacheRecords(m);
@@ -4934,7 +5484,11 @@ mDNSexport void mDNSCoreRestartQuestion(mDNS *const m, DNSQuestion *q)
     if (mDNSOpaque16IsZero(q->TargetQID) && ActiveQuestion(q))
     {
         q->ThisQInterval    = InitialQuestionInterval;  // MUST be > zero for an active question
     if (mDNSOpaque16IsZero(q->TargetQID) && ActiveQuestion(q))
     {
         q->ThisQInterval    = InitialQuestionInterval;  // MUST be > zero for an active question
-        q->RequestUnicast   = 2;                        // Set to 2 because is decremented once *before* we check it
+#if mDNS_REQUEST_UNICAST_RESPONSE
+        q->RequestUnicast = SET_QU_IN_FIRST_FOUR_QUERIES;
+#else   // mDNS_REQUEST_UNICAST_RESPONSE
+        q->RequestUnicast   = SET_QU_IN_FIRST_QUERY;
+#endif  // mDNS_REQUEST_UNICAST_RESPONSE
         q->LastQTime        = m->timenow - q->ThisQInterval;
         q->RecentAnswerPkts = 0;
         ExpireDupSuppressInfo(q->DupSuppress, m->timenow);
         q->LastQTime        = m->timenow - q->ThisQInterval;
         q->RecentAnswerPkts = 0;
         ExpireDupSuppressInfo(q->DupSuppress, m->timenow);
@@ -4953,9 +5507,13 @@ mDNSexport void mDNSCoreRestartRegistration(mDNS *const m, AuthRecord  *rr, int
         // announceCount < 0 indicates default announce count should be used
         if (announceCount < 0)
             announceCount = InitialAnnounceCount;
         // announceCount < 0 indicates default announce count should be used
         if (announceCount < 0)
             announceCount = InitialAnnounceCount;
-        if (rr->AnnounceCount < announceCount)
+        if (rr->AnnounceCount < announceCount) 
             rr->AnnounceCount  = announceCount;
             rr->AnnounceCount  = announceCount;
-        rr->AnnounceCount  = InitialAnnounceCount;
+
+        if (mDNS_KeepaliveRecord(&rr->resrec))
+            rr->AnnounceCount  = 0; // Do not announce keepalive records
+        else
+            rr->AnnounceCount  = InitialAnnounceCount;
         rr->SendNSECNow    = mDNSNULL;
         InitializeLastAPTime(m, rr);
     }
         rr->SendNSECNow    = mDNSNULL;
         InitializeLastAPTime(m, rr);
     }
@@ -4982,7 +5540,7 @@ mDNSexport void mDNS_UpdateAllowSleep(mDNS *const m)
         {
             allowSleep = mDNSfalse;
             mDNS_snprintf(reason, sizeof(reason), "sleep proxy for %d records", 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);
+            LogInfo("mDNS_UpdateAllowSleep: Sleep disabled because we are proxying %d records", m->ProxyRecords);
         }
 
         if (allowSleep && mDNSCoreHaveAdvertisedMulticastServices(m))
         }
 
         if (allowSleep && mDNSCoreHaveAdvertisedMulticastServices(m))
@@ -4991,14 +5549,14 @@ mDNSexport void mDNS_UpdateAllowSleep(mDNS *const m)
             NetworkInterfaceInfo *intf;
             for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next))
             {
             NetworkInterfaceInfo *intf;
             for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next))
             {
-                if (intf->McastTxRx && !intf->Loopback)
+                if (intf->McastTxRx && !intf->Loopback && !mDNSPlatformInterfaceIsD2D(intf->InterfaceID))
                 {
                     // 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);
                 {
                     // 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);
+                        LogInfo("mDNS_UpdateAllowSleep: Sleep disabled because %s does not support NetWake", intf->ifname);
                         break;
                     }
 
                         break;
                     }
 
@@ -5006,8 +5564,8 @@ mDNSexport void mDNS_UpdateAllowSleep(mDNS *const m)
                     if (FindSPSInCache1(m, &intf->NetWakeBrowse, mDNSNULL, mDNSNULL) == mDNSNULL)
                     {
                         allowSleep = mDNSfalse;
                     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);
+                        mDNS_snprintf(reason, sizeof(reason), "No sleep proxy server on %s", intf->ifname);
+                        LogInfo("mDNS_UpdateAllowSleep: Sleep disabled because %s has no sleep proxy server", intf->ifname);
                         break;
                     }
                 }
                         break;
                     }
                 }
@@ -5017,7 +5575,10 @@ mDNSexport void mDNS_UpdateAllowSleep(mDNS *const m)
 
     // Call the platform code to enable/disable sleep
     mDNSPlatformSetAllowSleep(m, allowSleep, reason);
 
     // Call the platform code to enable/disable sleep
     mDNSPlatformSetAllowSleep(m, allowSleep, reason);
+#else
+       (void) m;
 #endif /* !defined(IDLESLEEPCONTROL_DISABLED) */
 #endif /* !defined(IDLESLEEPCONTROL_DISABLED) */
+       
 }
 
 mDNSlocal mDNSBool mDNSUpdateOkToSend(mDNS *const m, AuthRecord *rr, NetworkInterfaceInfo *const intf, mDNSu32 scopeid)
 }
 
 mDNSlocal mDNSBool mDNSUpdateOkToSend(mDNS *const m, AuthRecord *rr, NetworkInterfaceInfo *const intf, mDNSu32 scopeid)
@@ -5025,8 +5586,11 @@ mDNSlocal mDNSBool mDNSUpdateOkToSend(mDNS *const m, AuthRecord *rr, NetworkInte
     // If it is not a uDNS record, check to see if the updateid is zero. "updateid" is cleared when we have
     // sent the resource record on all the interfaces. If the update id is not zero, check to see if it is time
     // to send.
     // If it is not a uDNS record, check to see if the updateid is zero. "updateid" is cleared when we have
     // sent the resource record on all the interfaces. If the update id is not zero, check to see if it is time
     // to send.
-    if (AuthRecord_uDNS(rr) || mDNSOpaque16IsZero(rr->updateid) || m->timenow - (rr->LastAPTime + rr->ThisAPInterval) < 0)
+    if (AuthRecord_uDNS(rr) || (rr->AuthFlags & AuthFlagsWakeOnly) || mDNSOpaque16IsZero(rr->updateid) || 
+        m->timenow - (rr->LastAPTime + rr->ThisAPInterval) < 0)
+    {
         return mDNSfalse;
         return mDNSfalse;
+    }
 
     // If we have a pending registration for "scopeid", it is ok to send the update on that interface.
     // If the scopeid is too big to check for validity, we don't check against updateIntID. When
 
     // If we have a pending registration for "scopeid", it is ok to send the update on that interface.
     // If the scopeid is too big to check for validity, we don't check against updateIntID. When
@@ -5043,10 +5607,44 @@ mDNSlocal mDNSBool mDNSUpdateOkToSend(mDNS *const m, AuthRecord *rr, NetworkInte
     return mDNSfalse;
 }
 
     return mDNSfalse;
 }
 
-mDNSlocal mStatus UpdateKeepaliveRData(mDNS *const m, AuthRecord *rr, NetworkInterfaceInfo *const intf)
+mDNSexport void UpdateRMACCallback(mDNS *const m, void *context)
+{
+    IPAddressMACMapping *addrmap = (IPAddressMACMapping *)context ;
+    m->CurrentRecord = m->ResourceRecords;
+
+    if (!addrmap)
+    {
+        LogMsg("UpdateRMACCallback: Address mapping is NULL");
+        return;
+    }
+
+    while (m->CurrentRecord)
+    {
+        AuthRecord *rr = m->CurrentRecord;
+        // If this is a keepalive record and the remote IP address matches, update the RData
+        if (mDNS_KeepaliveRecord(&rr->resrec))
+        {
+            mDNSAddr raddr;
+            getKeepaliveRaddr(m, rr, &raddr);
+            if (mDNSSameAddress(&raddr, &addrmap->ipaddr))
+            {
+                UpdateKeepaliveRData(m, rr, mDNSNULL, mDNStrue, (char *)(addrmap->ethaddr));
+            }
+        }
+        m->CurrentRecord = rr->next;
+    }
+
+    if (addrmap)
+    {
+        mDNSPlatformMemFree(addrmap);
+    }
+}
+
+mDNSexport mStatus UpdateKeepaliveRData(mDNS *const m, AuthRecord *rr, NetworkInterfaceInfo *const intf, mDNSBool updateMac, char *ethAddr)
 {
     mDNSu16 newrdlength;
     mDNSAddr laddr, raddr;
 {
     mDNSu16 newrdlength;
     mDNSAddr laddr, raddr;
+    mDNSEthAddr eth;
     mDNSIPPort lport, rport;
     mDNSu32 timeout, seq, ack;
     mDNSu16 win;
     mDNSIPPort lport, rport;
     mDNSu32 timeout, seq, ack;
     mDNSu16 win;
@@ -5056,21 +5654,9 @@ mDNSlocal mStatus UpdateKeepaliveRData(mDNS *const m, AuthRecord *rr, NetworkInt
     mDNSTCPInfo mti;
     mStatus ret;
 
     mDNSTCPInfo mti;
     mStatus ret;
 
-    if (rr->NewRData)
-    {
-        RData *n = rr->NewRData;
-
-        LogMsg("UpdateKeepaliveRData: Update was queued on %s", ARDisplayString(m, rr));
-
-        rr->NewRData = mDNSNULL;
-        if (rr->UpdateCallback)
-            rr->UpdateCallback(m, rr, n, rr->newrdlength);
-    }
-
     // Note: If we fail to update the  DNS NULL  record with additional information in this function, it will be registered
     // with the SPS like any other record. SPS will not send keepalives if it does not have additional information.
     // Note: If we fail to update the  DNS NULL  record with additional information in this function, it will be registered
     // with the SPS like any other record. SPS will not send keepalives if it does not have additional information.
-
-    mDNS_ExtractKeepaliveInfo(rr, &timeout, &laddr, &raddr, &seq, &ack, &lport, &rport, &win);
+    mDNS_ExtractKeepaliveInfo(rr, &timeout, &laddr, &raddr, &eth, &seq, &ack, &lport, &rport, &win);
     if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(&raddr) || mDNSIPPortIsZero(lport) ||
         mDNSIPPortIsZero(rport))
     {
     if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(&raddr) || mDNSIPPortIsZero(lport) ||
         mDNSIPPortIsZero(rport))
     {
@@ -5078,27 +5664,37 @@ mDNSlocal mStatus UpdateKeepaliveRData(mDNS *const m, AuthRecord *rr, NetworkInt
         return mStatus_UnknownErr;
     }
 
         return mStatus_UnknownErr;
     }
 
-    // If this keepalive packet would be sent on a different interface than the current one that we are processing
-    // now, then we don't update the DNS NULL record. But we do not prevent it from registering with the SPS. When SPS sees
-    // this DNS NULL record, it does not send any keepalives as it does not have all the information
-
-    ret = mDNSPlatformRetrieveTCPInfo(m, &laddr, &lport, &raddr, &rport, &mti);
-    if (ret != mStatus_NoError)
+    if (updateMac)
     {
     {
-        LogMsg("mDNSPlatformRetrieveTCPInfo: mDNSPlatformRetrieveTCPInfo failed %d", ret);
-        return ret;
-    }
+        if (laddr.type == mDNSAddrType_IPv4)
+            newrdlength = mDNS_snprintf((char *)&txt.c[1], sizeof(txt.c) - 1, "t=%d i=%d c=%d h=%#a d=%#a l=%u r=%u m=%s", timeout, kKeepaliveRetryInterval, kKeepaliveRetryCount, &laddr, &raddr, mDNSVal16(lport), mDNSVal16(rport), ethAddr);
+        else
+            newrdlength = mDNS_snprintf((char *)&txt.c[1], sizeof(txt.c) - 1, "t=%d i=%d c=%d H=%#a D=%#a l=%u r=%u m=%s", timeout, kKeepaliveRetryInterval, kKeepaliveRetryCount, &laddr, &raddr,  mDNSVal16(lport), mDNSVal16(rport), ethAddr);
 
 
-    if (mti.IntfId != intf->InterfaceID)
-    {
-        LogInfo("mDNSPlatformRetrieveTCPInfo: InterfaceID mismatch mti %p, Interface %p", mti.IntfId, intf->InterfaceID);
-        return mStatus_BadParamErr;
     }
     }
-
-    if (laddr.type == mDNSAddrType_IPv4)
-        newrdlength = mDNS_snprintf((char *)&txt.c[1], sizeof(txt.c) - 1, "t=%d h=%#a d=%#a l=%u r=%u s=%u a=%u w=%u", timeout, &laddr, &raddr, mDNSVal16(lport), mDNSVal16(rport), mti.seq, mti.ack, mti.window);
     else
     else
-        newrdlength = mDNS_snprintf((char *)&txt.c[1], sizeof(txt.c) - 1, "t=%d H=%#a D=%#a l=%u%u r=%u%u s=%u a=%u w=%u", timeout, &laddr, &raddr, lport.b[0], lport.b[1], rport.b[0], rport.b[1], rport.NotAnInteger, mti.seq, mti.ack, mti.window);
+    {
+        // If this keepalive packet would be sent on a different interface than the current one that we are processing
+        // now, then we don't update the DNS NULL record. But we do not prevent it from registering with the SPS. When SPS sees
+        // this DNS NULL record, it does not send any keepalives as it does not have all the information
+        mDNSPlatformMemZero(&mti, sizeof (mDNSTCPInfo));
+        ret = mDNSPlatformRetrieveTCPInfo(m, &laddr, &lport, &raddr, &rport, &mti);
+        if (ret != mStatus_NoError)
+        {
+            LogMsg("mDNSPlatformRetrieveTCPInfo: mDNSPlatformRetrieveTCPInfo failed %d", ret);
+            return ret;
+        }
+        if ((intf != mDNSNULL) && (mti.IntfId != intf->InterfaceID))
+        {
+            LogInfo("mDNSPlatformRetrieveTCPInfo: InterfaceID  mismatch mti.IntfId = %p InterfaceID = %p",  mti.IntfId, intf->InterfaceID);
+            return mStatus_BadParamErr;
+        }
+
+        if (laddr.type == mDNSAddrType_IPv4)
+            newrdlength = mDNS_snprintf((char *)&txt.c[1], sizeof(txt.c) - 1, "t=%d i=%d c=%d h=%#a d=%#a l=%u r=%u m=%.6a s=%u a=%u w=%u", timeout, kKeepaliveRetryInterval, kKeepaliveRetryCount, &laddr, &raddr, mDNSVal16(lport), mDNSVal16(rport), &eth, mti.seq, mti.ack, mti.window);
+        else
+            newrdlength = mDNS_snprintf((char *)&txt.c[1], sizeof(txt.c) - 1, "t=%d i=%d c=%d H=%#a D=%#a l=%u r=%u m=%.6a s=%u a=%u w=%u", timeout, kKeepaliveRetryInterval, kKeepaliveRetryCount, &laddr, &raddr, mDNSVal16(lport), mDNSVal16(rport), &eth, mti.seq, mti.ack, mti.window);
+    }
 
     // Did we insert a null byte at the end ?
     if (newrdlength == (sizeof(txt.c) - 1))
 
     // Did we insert a null byte at the end ?
     if (newrdlength == (sizeof(txt.c) - 1))
@@ -5119,16 +5715,16 @@ mDNSlocal mStatus UpdateKeepaliveRData(mDNS *const m, AuthRecord *rr, NetworkInt
     newrd->MaxRDLength = (mDNSu16) rdsize;
     mDNSPlatformMemCopy(&newrd->u, txt.c, newrdlength);
 
     newrd->MaxRDLength = (mDNSu16) rdsize;
     mDNSPlatformMemCopy(&newrd->u, txt.c, newrdlength);
 
-    rr->NewRData  = newrd;
-    rr->newrdlength = newrdlength;
-    if (!ValidateRData(rr->resrec.rrtype, newrdlength, newrd))
+    //  If we are updating the record for the first time, rdata points to rdatastorage as the rdata memory
+    //  was allocated as part of the AuthRecord itself. We allocate memory when we update the AuthRecord.
+    //  If the resource record has data that we allocated in a previous pass (to update MAC address),
+    //  free that memory here before copying in the new data.
+    if ( rr->resrec.rdata != &rr->rdatastorage)
     {
     {
-        LogMsg("UpdateKeepaliveRData: ValidateRData failed %s", ARDisplayString(m, rr));
-        return mStatus_BadParamErr;
+        mDNSPlatformMemFree(rr->resrec.rdata);
+        LogSPS("UpdateKeepaliveRData: Freed allocated memory for keep alive packet: %s ", ARDisplayString(m, rr));
     }
     }
-
-    // We don't send goodbyes for non-shared records and hence updating here should be fine
-    CompleteRDataUpdate(m, rr);
+    SetNewRData(&rr->resrec, newrd, newrdlength);    // Update our rdata
 
     LogSPS("UpdateKeepaliveRData: successfully updated the record %s", ARDisplayString(m, rr));
     return mStatus_NoError;
 
     LogSPS("UpdateKeepaliveRData: successfully updated the record %s", ARDisplayString(m, rr));
     return mStatus_NoError;
@@ -5165,8 +5761,11 @@ mDNSlocal void SendSPSRegistrationForOwner(mDNS *const m, NetworkInterfaceInfo *
         // with different owner option gets different ID.
         msgid = mDNS_NewMessageID(m);
         for (rr = m->ResourceRecords; rr; rr=rr->next)
         // with different owner option gets different ID.
         msgid = mDNS_NewMessageID(m);
         for (rr = m->ResourceRecords; rr; rr=rr->next)
-            if (rr->resrec.RecordType > kDNSRecordTypeDeregistering)
+        {
+            if (!(rr->AuthFlags & AuthFlagsWakeOnly) && rr->resrec.RecordType > kDNSRecordTypeDeregistering)
+            {
                 if (rr->resrec.InterfaceID == intf->InterfaceID || (!rr->resrec.InterfaceID && (rr->ForceMCast || IsLocalDomain(rr->resrec.name))))
                 if (rr->resrec.InterfaceID == intf->InterfaceID || (!rr->resrec.InterfaceID && (rr->ForceMCast || IsLocalDomain(rr->resrec.name))))
+                {
                     if (mDNSPlatformMemSame(owner, &rr->WakeUp, sizeof(*owner)))
                     {
                         rr->SendRNow = mDNSInterfaceMark;   // mark it now
                     if (mDNSPlatformMemSame(owner, &rr->WakeUp, sizeof(*owner)))
                     {
                         rr->SendRNow = mDNSInterfaceMark;   // mark it now
@@ -5178,6 +5777,9 @@ mDNSlocal void SendSPSRegistrationForOwner(mDNS *const m, NetworkInterfaceInfo *
                         else
                             msgid = rr->updateid;
                     }
                         else
                             msgid = rr->updateid;
                     }
+                }
+            }
+        }
     }
     else
         msgid = id;
     }
     else
         msgid = id;
@@ -5200,12 +5802,13 @@ mDNSlocal void SendSPSRegistrationForOwner(mDNS *const m, NetworkInterfaceInfo *
                     const mDNSu8 *const limit = m->omsg.data + (m->omsg.h.mDNS_numUpdates ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData) - optspace;
 
                     // If we can't update the keepalive record, don't send it
                     const mDNSu8 *const limit = m->omsg.data + (m->omsg.h.mDNS_numUpdates ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData) - optspace;
 
                     // If we can't update the keepalive record, don't send it
-                    if (mDNS_KeepaliveRecord(&rr->resrec) && (UpdateKeepaliveRData(m, rr, intf) != mStatus_NoError))
+                    if (mDNS_KeepaliveRecord(&rr->resrec) && (UpdateKeepaliveRData(m, rr, intf, mDNSfalse, mDNSNULL) != mStatus_NoError))
                     {
                         if (scopeid < (sizeof(rr->updateIntID) * mDNSNBBY))
                         {
                             bit_clr_opaque64(rr->updateIntID, scopeid);
                         }
                     {
                         if (scopeid < (sizeof(rr->updateIntID) * mDNSNBBY))
                         {
                             bit_clr_opaque64(rr->updateIntID, scopeid);
                         }
+                        rr->SendRNow = mDNSNULL;
                         continue;
                     }
 
                         continue;
                     }
 
@@ -5290,6 +5893,7 @@ mDNSlocal mDNSBool RecordIsFirstOccurrenceOfOwner(mDNS *const m, const AuthRecor
 mDNSlocal void mDNSCoreStoreProxyRR(mDNS *const m, const mDNSInterfaceID InterfaceID, AuthRecord *const rr)
 {
     AuthRecord *newRR = mDNSPlatformMemAllocate(sizeof(AuthRecord));
 mDNSlocal void mDNSCoreStoreProxyRR(mDNS *const m, const mDNSInterfaceID InterfaceID, AuthRecord *const rr)
 {
     AuthRecord *newRR = mDNSPlatformMemAllocate(sizeof(AuthRecord));
+
     if (newRR == mDNSNULL)
     {
         LogSPS("%s : could not allocate memory for new resource record", __func__);
     if (newRR == mDNSNULL)
     {
         LogSPS("%s : could not allocate memory for new resource record", __func__);
@@ -5319,7 +5923,6 @@ mDNSlocal void mDNSCoreStoreProxyRR(mDNS *const m, const mDNSInterfaceID Interfa
     // Insert the new node at the head of the list.
     newRR->next        = m->SPSRRSet;
     m->SPSRRSet        = newRR;
     // Insert the new node at the head of the list.
     newRR->next        = m->SPSRRSet;
     m->SPSRRSet        = newRR;
-    m->ClearSPSRecords = 0;
     LogSPS("%s : Storing proxy record : %s ", __func__, ARDisplayString(m, rr));
 }
 
     LogSPS("%s : Storing proxy record : %s ", __func__, ARDisplayString(m, rr));
 }
 
@@ -5328,10 +5931,12 @@ mDNSlocal void mDNSCoreStoreProxyRR(mDNS *const m, const mDNSInterfaceID Interfa
 // updateIntID bit field tells us on which interfaces we need to register this record. When we get an
 // ack from the sleep proxy server, we clear the interface bit. This way, we know when a record completes
 // registration on all the interfaces
 // updateIntID bit field tells us on which interfaces we need to register this record. When we get an
 // ack from the sleep proxy server, we clear the interface bit. This way, we know when a record completes
 // registration on all the interfaces
-mDNSlocal void SPSInitRecordsBeforeUpdate(mDNS *const m, mDNSOpaque64 updateIntID)
+mDNSlocal void SPSInitRecordsBeforeUpdate(mDNS *const m, mDNSOpaque64 updateIntID, mDNSBool *WakeOnlyService)
 {
     AuthRecord *ar;
     LogSPS("SPSInitRecordsBeforeUpdate: UpdateIntID 0x%x 0x%x", updateIntID.l[1], updateIntID.l[0]);
 {
     AuthRecord *ar;
     LogSPS("SPSInitRecordsBeforeUpdate: UpdateIntID 0x%x 0x%x", updateIntID.l[1], updateIntID.l[0]);
+    
+    *WakeOnlyService = mDNSfalse;
 
     // Before we store the A and AAAA records that we are going to register with the sleep proxy,
     // make sure that the old sleep proxy records are removed.
 
     // Before we store the A and AAAA records that we are going to register with the sleep proxy,
     // make sure that the old sleep proxy records are removed.
@@ -5341,11 +5946,21 @@ mDNSlocal void SPSInitRecordsBeforeUpdate(mDNS *const m, mDNSOpaque64 updateIntI
     // never be registered on any other interface. For others, it should be sent on all interfaces.
     for (ar = m->ResourceRecords; ar; ar=ar->next)
     {
     // never be registered on any other interface. For others, it should be sent on all interfaces.
     for (ar = m->ResourceRecords; ar; ar=ar->next)
     {
+        ar->updateIntID = zeroOpaque64;
+        ar->updateid    = zeroID;
         if (AuthRecord_uDNS(ar))
         {
             continue;
         }
         if (AuthRecord_uDNS(ar))
         {
             continue;
         }
-        ar->updateid = zeroID;
+        if (ar->AuthFlags & AuthFlagsWakeOnly)
+        {
+            if (ar->resrec.RecordType == kDNSRecordTypeShared && ar->RequireGoodbye)
+            {
+                ar->ImmedAnswer = mDNSInterfaceMark;
+                *WakeOnlyService = mDNStrue;
+                continue;
+            }
+        }
         if (!ar->resrec.InterfaceID)
         {
             LogSPS("Setting scopeid (ALL) 0x%x 0x%x for %s", updateIntID.l[1], updateIntID.l[0], ARDisplayString(m, ar));
         if (!ar->resrec.InterfaceID)
         {
             LogSPS("Setting scopeid (ALL) 0x%x 0x%x for %s", updateIntID.l[1], updateIntID.l[0], ARDisplayString(m, ar));
@@ -5358,9 +5973,9 @@ mDNSlocal void SPSInitRecordsBeforeUpdate(mDNS *const m, mDNSOpaque64 updateIntI
             mDNSu32 scopeid = mDNSPlatformInterfaceIndexfromInterfaceID(m, ar->resrec.InterfaceID, mDNStrue);
             if ((scopeid < (sizeof(updateIntID) * mDNSNBBY)) && bit_get_opaque64(updateIntID, scopeid))
             {
             mDNSu32 scopeid = mDNSPlatformInterfaceIndexfromInterfaceID(m, ar->resrec.InterfaceID, mDNStrue);
             if ((scopeid < (sizeof(updateIntID) * mDNSNBBY)) && bit_get_opaque64(updateIntID, scopeid))
             {
-                ar->updateIntID = zeroOpaque64;
                 bit_set_opaque64(ar->updateIntID, scopeid);
                 bit_set_opaque64(ar->updateIntID, scopeid);
-                LogSPS("Setting scopeid(%d) 0x%x 0x%x for %s", scopeid, ar->updateIntID.l[1], ar->updateIntID.l[0], ARDisplayString(m, ar));
+                LogSPS("SPSInitRecordsBeforeUpdate: Setting scopeid(%d) 0x%x 0x%x for %s", scopeid, ar->updateIntID.l[1],
+                    ar->updateIntID.l[0], ARDisplayString(m, ar));
             }
             else
             {
             }
             else
             {
@@ -5441,7 +6056,7 @@ mDNSlocal void NetWakeResolve(mDNS *const m, DNSQuestion *question, const Resour
 
     if (answer->rrtype == kDNSType_SRV)
     {
 
     if (answer->rrtype == kDNSType_SRV)
     {
-        // 1. Got the SRV record; now look up the target host's IP  address
+        // 1. Got the SRV record; now look up the target host's IP address
         mDNS_StopQuery(m, question);
         intf->SPSPort[sps] = answer->rdata->u.srv.port;
         AssignDomainName(&question->qname, &answer->rdata->u.srv.target);
         mDNS_StopQuery(m, question);
         intf->SPSPort[sps] = answer->rdata->u.srv.port;
         AssignDomainName(&question->qname, &answer->rdata->u.srv.target);
@@ -5461,7 +6076,7 @@ mDNSlocal void NetWakeResolve(mDNS *const m, DNSQuestion *question, const Resour
     }
     else if (answer->rrtype == kDNSType_A && answer->rdlength == 0)
     {
     }
     else if (answer->rrtype == kDNSType_A && answer->rdlength == 0)
     {
-        // 3. Got negative response -- target host apparently has IPv4 disabled -- so try looking up the target host's IPv6 address(es) instead
+        // 3. Got negative response -- target host apparently has IPv6 disabled -- so try looking up the target host's IPv4 address(es) instead
         mDNS_StopQuery(m, question);
         LogSPS("NetWakeResolve: SPS %d %##s has no IPv4 address, will try IPv6 instead", sps, question->qname.c);
         question->qtype = kDNSType_AAAA;
         mDNS_StopQuery(m, question);
         LogSPS("NetWakeResolve: SPS %d %##s has no IPv4 address, will try IPv6 instead", sps, question->qname.c);
         question->qtype = kDNSType_AAAA;
@@ -5489,32 +6104,68 @@ mDNSexport mDNSBool mDNSCoreHaveAdvertisedMulticastServices(mDNS *const m)
     return mDNSfalse;
 }
 
     return mDNSfalse;
 }
 
-mDNSlocal void SendSleepGoodbyes(mDNS *const m)
+#ifdef APPLE_OSX_mDNSResponder
+// This function is used only in the case of local NIC proxy. For external
+// sleep proxy server, we do this in SPSInitRecordsBeforeUpdate when we
+// walk the resource records.
+mDNSlocal void SendGoodbyesForWakeOnlyService(mDNS *const m, mDNSBool *WakeOnlyService)
 {
     AuthRecord *rr;
 {
     AuthRecord *rr;
-    m->SleepState = SleepState_Sleeping;
 
 
-#ifndef UNICAST_DISABLED
-    SleepRecordRegistrations(m);    // If we have no SPS, need to deregister our uDNS records
-#endif /* UNICAST_DISABLED */
+    *WakeOnlyService = mDNSfalse;
 
     // Mark all the records we need to deregister and send them
     for (rr = m->ResourceRecords; rr; rr=rr->next)
 
     // Mark all the records we need to deregister and send them
     for (rr = m->ResourceRecords; rr; rr=rr->next)
-        if (rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye)
+    {
+        if ((rr->AuthFlags & AuthFlagsWakeOnly) && 
+            rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye)
+        {
             rr->ImmedAnswer = mDNSInterfaceMark;
             rr->ImmedAnswer = mDNSInterfaceMark;
-    SendResponses(m);
+            *WakeOnlyService = mDNStrue;
+        }
+    }
 }
 }
+#endif // APPLE_OSx_mDNSResponder
 
 
-/*
- * This function attempts to detect if multiple interfaces are on the same subnet.
- * It makes this determination based only on the IPv4 Addresses and subnet masks.
- * IPv6 link local addresses that are configured by default on all interfaces make
- * it hard to make this determination
- *
- * The 'real' fix for this would be to send out multicast packets over one interface
- * and conclude that multiple interfaces are on the same subnet only if these packets
- * are seen on other interfaces on the same system
- */
+mDNSlocal void SendSleepGoodbyes(mDNS *const m, mDNSBool AllInterfaces, mDNSBool unicast)
+{
+    AuthRecord *rr;
+    m->SleepState = SleepState_Sleeping;
+
+    // If AllInterfaces is not set, the caller has already marked it appropriately
+    // on which interfaces this should be sent.
+    if (AllInterfaces)
+    {
+        NetworkInterfaceInfo *intf;
+        for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next))
+        {
+            intf->SendGoodbyes = 1;
+        }
+    }
+    if (unicast)
+    {
+#ifndef UNICAST_DISABLED
+        SleepRecordRegistrations(m);    // If we have no SPS, need to deregister our uDNS records
+#endif /* UNICAST_DISABLED */
+    }
+
+    // Mark all the records we need to deregister and send them
+    for (rr = m->ResourceRecords; rr; rr=rr->next)
+        if (rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye)
+            rr->ImmedAnswer = mDNSInterfaceMark;
+    SendResponses(m);
+}
+
+/*
+ * This function attempts to detect if multiple interfaces are on the same subnet.
+ * It makes this determination based only on the IPv4 Addresses and subnet masks.
+ * IPv6 link local addresses that are configured by default on all interfaces make
+ * it hard to make this determination
+ *
+ * The 'real' fix for this would be to send out multicast packets over one interface
+ * and conclude that multiple interfaces are on the same subnet only if these packets
+ * are seen on other interfaces on the same system
+ */
 mDNSlocal mDNSBool skipSameSubnetRegistration(mDNS *const m, mDNSInterfaceID *regID, mDNSu32 count, mDNSInterfaceID intfid)
 {
     NetworkInterfaceInfo *intf;
 mDNSlocal mDNSBool skipSameSubnetRegistration(mDNS *const m, mDNSInterfaceID *regID, mDNSu32 count, mDNSInterfaceID intfid)
 {
     NetworkInterfaceInfo *intf;
@@ -5548,14 +6199,35 @@ mDNSlocal mDNSBool skipSameSubnetRegistration(mDNS *const m, mDNSInterfaceID *re
     return (mDNSfalse);
 }
 
     return (mDNSfalse);
 }
 
+mDNSlocal void DoKeepaliveCallbacks(mDNS *m)
+{
+    // Loop through the keepalive records and callback with an error
+    m->CurrentRecord = m->ResourceRecords;
+    while (m->CurrentRecord)
+    {
+        AuthRecord *const rr = m->CurrentRecord;
+        if ((mDNS_KeepaliveRecord(&rr->resrec)) && (rr->resrec.RecordType != kDNSRecordTypeDeregistering))
+        {
+            LogSPS("DoKeepaliveCallbacks: Invoking the callback for %s", ARDisplayString(m, rr));
+            if (rr->RecordCallback)
+                rr->RecordCallback(m, rr, mStatus_BadStateErr);
+        }
+        if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now
+            m->CurrentRecord = rr->next;
+    }
+}
+
 // BeginSleepProcessing is called, with the lock held, from either mDNS_Execute or mDNSCoreMachineSleep
 mDNSlocal void BeginSleepProcessing(mDNS *const m)
 {
     mDNSBool SendGoodbyes = mDNStrue;
 // BeginSleepProcessing is called, with the lock held, from either mDNS_Execute or mDNSCoreMachineSleep
 mDNSlocal void BeginSleepProcessing(mDNS *const m)
 {
     mDNSBool SendGoodbyes = mDNStrue;
+    mDNSBool WakeOnlyService  = mDNSfalse;
+    mDNSBool invokeKACallback = mDNStrue;
     const CacheRecord *sps[3] = { mDNSNULL };
     mDNSOpaque64 updateIntID = zeroOpaque64;
     mDNSInterfaceID registeredIntfIDS[128];
     mDNSu32 registeredCount = 0;
     const CacheRecord *sps[3] = { mDNSNULL };
     mDNSOpaque64 updateIntID = zeroOpaque64;
     mDNSInterfaceID registeredIntfIDS[128];
     mDNSu32 registeredCount = 0;
+    int skippedRegistrations = 0;
 
     m->NextScheduledSPRetry = m->timenow;
 
 
     m->NextScheduledSPRetry = m->timenow;
 
@@ -5566,7 +6238,31 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m)
         NetworkInterfaceInfo *intf;
         for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next))
         {
         NetworkInterfaceInfo *intf;
         for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next))
         {
-            if (!intf->NetWake) LogSPS("BeginSleepProcessing: %-6s not capable of magic packet wakeup", intf->ifname);
+            // Intialize it to false. These values make sense only when SleepState is set to Sleeping.
+            intf->SendGoodbyes = 0;
+
+            // If it is not multicast capable, we could not have possibly discovered sleep proxy
+            // servers.
+            if (!intf->McastTxRx || mDNSPlatformInterfaceIsD2D(intf->InterfaceID))
+            {
+                LogSPS("BeginSleepProcessing: %-6s Ignoring for registrations", intf->ifname);
+                continue;
+            }
+
+            // If we are not capable of WOMP, then don't register with sleep proxy.
+            //
+            // Note: If we are not NetWake capable, we don't browse for the sleep proxy server.
+            // We might find sleep proxy servers in the cache and start a resolve on them.
+            // But then if the interface goes away, we won't stop these questions because
+            // mDNS_DeactivateNetWake_internal assumes that a browse has been started for it
+            // to stop both the browse and resolve questions.
+            if (!intf->NetWake)
+            {
+                LogSPS("BeginSleepProcessing: %-6s not capable of magic packet wakeup", intf->ifname);
+                intf->SendGoodbyes = 1;
+                skippedRegistrations++;
+                continue;
+            }
 
             // Check if we have already registered with a sleep proxy for this subnet
             if (skipSameSubnetRegistration(m, registeredIntfIDS, registeredCount, intf->InterfaceID))
 
             // Check if we have already registered with a sleep proxy for this subnet
             if (skipSameSubnetRegistration(m, registeredIntfIDS, registeredCount, intf->InterfaceID))
@@ -5576,20 +6272,33 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m)
             }
 
 #if APPLE_OSX_mDNSResponder
             }
 
 #if APPLE_OSX_mDNSResponder
-            else if (ActivateLocalProxy(m, intf->ifname) == mStatus_NoError)
+            else if (SupportsInNICProxy(intf))
             {
             {
-                SendGoodbyes = mDNSfalse;
-                LogSPS("BeginSleepProcessing: %-6s using local proxy", intf->ifname);
-                // This will leave m->SleepState set to SleepState_Transferring,
-                // which is okay because with no outstanding resolves, or updates in flight,
-                // mDNSCoreReadyForSleep() will conclude correctly that all the updates have already completed
-
-                registeredIntfIDS[registeredCount] = intf->InterfaceID;
-                registeredCount++;
+                if (ActivateLocalProxy(m, intf) == mStatus_NoError)
+                {
+                    SendGoodbyesForWakeOnlyService(m, &WakeOnlyService);
+                    SendGoodbyes = mDNSfalse;
+                    invokeKACallback  = mDNSfalse;
+                    LogSPS("BeginSleepProcessing: %-6s using local proxy", intf->ifname);
+                    // This will leave m->SleepState set to SleepState_Transferring,
+                    // which is okay because with no outstanding resolves, or updates in flight,
+                    // mDNSCoreReadyForSleep() will conclude correctly that all the updates have already completed
+
+                    registeredIntfIDS[registeredCount] = intf->InterfaceID;
+                    registeredCount++;
+                }
             }
 #endif // APPLE_OSX_mDNSResponder
             else
             {
             }
 #endif // APPLE_OSX_mDNSResponder
             else
             {
+#if APPLE_OSX_mDNSResponder
+                // If on battery, do not attempt to offload to external sleep proxies
+                if (m->SystemWakeOnLANEnabled == mDNS_WakeOnBattery)
+                {
+                    LogSPS("BegingSleepProcessing: Not connected to AC power - Not registering with an external sleep proxy.");
+                    return;
+                }
+#endif // APPLE_OSX_mDNSResponder
                 FindSPSInCache(m, &intf->NetWakeBrowse, sps);
                 if (!sps[0]) LogSPS("BeginSleepProcessing: %-6s %#a No Sleep Proxy Server found (Next Browse Q in %d, interval %d)",
                                     intf->ifname, &intf->ip, NextQSendTime(&intf->NetWakeBrowse) - m->timenow, intf->NetWakeBrowse.ThisQInterval);
                 FindSPSInCache(m, &intf->NetWakeBrowse, sps);
                 if (!sps[0]) LogSPS("BeginSleepProcessing: %-6s %#a No Sleep Proxy Server found (Next Browse Q in %d, interval %d)",
                                     intf->ifname, &intf->ip, NextQSendTime(&intf->NetWakeBrowse) - m->timenow, intf->NetWakeBrowse.ThisQInterval);
@@ -5601,10 +6310,6 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m)
                     intf->NextSPSAttempt = 0;
                     intf->NextSPSAttemptTime = m->timenow + mDNSPlatformOneSecond;
 
                     intf->NextSPSAttempt = 0;
                     intf->NextSPSAttemptTime = m->timenow + mDNSPlatformOneSecond;
 
-#if APPLE_OSX_mDNSResponder
-                    // Before we start the sleep processing, stop IPv6 advertisements
-                    mDNSPlatformToggleInterfaceAdvt(m, mDNStrue);
-#endif
                     scopeid = mDNSPlatformInterfaceIndexfromInterfaceID(m, intf->InterfaceID, mDNStrue);
                     // Now we know for sure that we have to wait for registration to complete on this interface.
                     if (scopeid < (sizeof(updateIntID) * mDNSNBBY))
                     scopeid = mDNSPlatformInterfaceIndexfromInterfaceID(m, intf->InterfaceID, mDNStrue);
                     // Now we know for sure that we have to wait for registration to complete on this interface.
                     if (scopeid < (sizeof(updateIntID) * mDNSNBBY))
@@ -5641,12 +6346,47 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m)
 
     // If we have at least one interface on which we are registering with an external sleep proxy,
     // initialize all the records appropriately.
 
     // If we have at least one interface on which we are registering with an external sleep proxy,
     // initialize all the records appropriately.
-    if (!mDNSOpaque64IsZero(&updateIntID)) SPSInitRecordsBeforeUpdate(m, updateIntID);
+    if (!mDNSOpaque64IsZero(&updateIntID))
+        SPSInitRecordsBeforeUpdate(m, updateIntID, &WakeOnlyService);
 
 
-    if (SendGoodbyes)   // If we didn't find even one Sleep Proxy
+    // Call the applicaitons that registered a keepalive record to inform them that we failed to offload
+    // the records to a sleep proxy.
+    if (invokeKACallback)
+    {
+        LogSPS("BeginSleepProcessing: Did not register with an in-NIC proxy - invoking the callbacks for KA records");
+        DoKeepaliveCallbacks(m);
+    }
+
+    // SendSleepGoodbyes last two arguments control whether we send goodbyes on all
+    // interfaces and also deregister unicast registrations.
+    //
+    // - If there are no sleep proxy servers, then send goodbyes on all interfaces
+    //   for both multicast and unicast.
+    // 
+    // - If we skipped registrations on some interfaces, then we have already marked
+    //   them appropriately above. We don't need to send goodbyes for unicast as
+    //   we have registered with at least one sleep proxy.
+    //
+    // - If we are not planning to send any goodbyes, then check for WakeOnlyServices.
+    //
+    // Note: If we are planning to send goodbyes, we mark the record with mDNSInterfaceAny
+    // and call SendResponses which inturn calls ShouldSendGoodbyesBeforeSleep which looks
+    // at WakeOnlyServices first.
+    if (SendGoodbyes)
     {
         LogSPS("BeginSleepProcessing: Not registering with Sleep Proxy Server");
     {
         LogSPS("BeginSleepProcessing: Not registering with Sleep Proxy Server");
-        SendSleepGoodbyes(m);
+        SendSleepGoodbyes(m, mDNStrue, mDNStrue);
+    }
+    else if (skippedRegistrations)
+    {
+        LogSPS("BeginSleepProcessing: Not registering with Sleep Proxy Server on all interfaces");
+        SendSleepGoodbyes(m, mDNSfalse, mDNSfalse);
+    }
+    else if (WakeOnlyService)
+    {
+        // If we saw WakeOnly service above, send the goodbyes now.
+        LogSPS("BeginSleepProcessing: Sending goodbyes for WakeOnlyServices");
+        SendResponses(m);
     }
 }
 
     }
 }
 
@@ -5668,7 +6408,11 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep)
             mDNSu8 oldstate = m->SPSState;
             mDNS_DropLockBeforeCallback();      // mDNS_DeregisterService expects to be called without the lock held, so we emulate that here
             m->SPSState = 2;
             mDNSu8 oldstate = m->SPSState;
             mDNS_DropLockBeforeCallback();      // mDNS_DeregisterService expects to be called without the lock held, so we emulate that here
             m->SPSState = 2;
+#ifndef SPC_DISABLED
             if (oldstate == 1) mDNS_DeregisterService(m, &m->SPSRecords);
             if (oldstate == 1) mDNS_DeregisterService(m, &m->SPSRecords);
+#else
+                       (void)oldstate;
+#endif
             mDNS_ReclaimLockAfterCallback();
         }
 
             mDNS_ReclaimLockAfterCallback();
         }
 
@@ -5683,6 +6427,7 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep)
         {
             m->DelaySleep = 0;
             m->SleepLimit = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 10);
         {
             m->DelaySleep = 0;
             m->SleepLimit = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 10);
+            m->mDNSStats.Sleeps++;
             BeginSleepProcessing(m);
         }
 
             BeginSleepProcessing(m);
         }
 
@@ -5703,6 +6448,7 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep)
         CacheGroup *cg;
         CacheRecord *cr;
         NetworkInterfaceInfo *intf;
         CacheGroup *cg;
         CacheRecord *cr;
         NetworkInterfaceInfo *intf;
+        mDNSs32 currtime, diff;
 
         mDNS_Lock(m);
         // Reset SleepLimit back to 0 now that we're awake again.
 
         mDNS_Lock(m);
         // Reset SleepLimit back to 0 now that we're awake again.
@@ -5725,6 +6471,7 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep)
             m->SPSState = 0;
             mDNSCoreBeSleepProxyServer_internal(m, m->SPSType, m->SPSPortability, m->SPSMarginalPower, m->SPSTotalPower, m->SPSFeatureFlags);
         }
             m->SPSState = 0;
             mDNSCoreBeSleepProxyServer_internal(m, m->SPSType, m->SPSPortability, m->SPSMarginalPower, m->SPSTotalPower, m->SPSFeatureFlags);
         }
+        m->mDNSStats.Wakes++;
 
         // ... and the same for NextSPSAttempt
         for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) intf->NextSPSAttempt = -1;
 
         // ... and the same for NextSPSAttempt
         for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) intf->NextSPSAttempt = -1;
@@ -5737,13 +6484,71 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep)
         LogInfo("mDNSCoreMachineSleep waking: NextSRVUpdate in %d %d", m->NextSRVUpdate - m->timenow, m->timenow);
 
         // 2. Re-validate our cache records
         LogInfo("mDNSCoreMachineSleep waking: NextSRVUpdate in %d %d", m->NextSRVUpdate - m->timenow, m->timenow);
 
         // 2. Re-validate our cache records
+        currtime = mDNSPlatformUTC();
+
+#if APPLE_OSX_mDNSResponder
+        // start time of this statistics gathering interval
+        m->StatStartTime = currtime;
+#endif // APPLE_OSX_mDNSResponder
+
+        diff = currtime - m->TimeSlept;
         FORALL_CACHERECORDS(slot, cg, cr)
         {
         FORALL_CACHERECORDS(slot, cg, cr)
         {
-            mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForWake);
+            // Temporary fix: For unicast cache records, look at how much time we slept.
+            // Adjust the RecvTime by the amount of time we slept so that we age the
+            // cache record appropriately. If it is expired already, purge. If there
+            // is a network change that happens after the wakeup, we might purge the
+            // cache anyways and this helps only in the case where there are no network
+            // changes across sleep/wakeup transition.
+            //
+            // Note: If there is a network/DNS server change that already happened and
+            // these cache entries are already refreshed and we are getting a delayed
+            // wake up notification, we might adjust the TimeRcvd based on the time slept
+            // now which can cause the cache to purge pre-maturely. As this is not a very
+            // common case, this should happen rarely.
+            if (!cr->resrec.InterfaceID)
+            {
+                if (diff > 0)
+                {
+                    mDNSu32 uTTL = RRUnadjustedTTL(cr->resrec.rroriginalttl);
+                    const mDNSs32 remain = uTTL - (m->timenow - cr->TimeRcvd) / mDNSPlatformOneSecond;
+
+                    // -if we have slept longer than the remaining TTL, purge and start fresh. 
+                    // -if we have been sleeping for a long time, we could reduce TimeRcvd below by
+                    //  a sufficiently big value which could cause the value to go into the future
+                    //  because of the signed comparison of time. For this to happen, we should have been
+                    //  sleeping really long (~24 days). For now, we want to be conservative and flush even
+                    //  if we have slept for more than two days.
+
+                    if (diff >= remain || diff > (2 * 24 * 3600))
+                    {
+                        LogInfo("mDNSCoreMachineSleep: %s: Purging cache entry SleptTime %d, Remaining TTL %d",
+                            CRDisplayString(m, cr), diff, remain);
+                        mDNS_PurgeCacheResourceRecord(m, cr);
+                        continue;
+                    }
+                    cr->TimeRcvd -= (diff * mDNSPlatformOneSecond);
+                    if (m->timenow - (cr->TimeRcvd + ((mDNSs32)uTTL * mDNSPlatformOneSecond)) >= 0)
+                    {
+                        LogInfo("mDNSCoreMachineSleep: %s: Purging after adjusting the remaining TTL %d by %d seconds",
+                            CRDisplayString(m, cr), remain, diff);
+                        mDNS_PurgeCacheResourceRecord(m, cr);
+                    }
+                    else
+                    {
+                        LogInfo("mDNSCoreMachineSleep: %s: Adjusted the remain ttl %u by %d seconds", CRDisplayString(m, cr), remain, diff);
+                    }
+                }
+            }
+            else
+            {
+                mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForWake);
+            }
         }
 
         // 3. Retrigger probing and announcing for all our authoritative records
         for (rr = m->ResourceRecords; rr; rr=rr->next)
         }
 
         // 3. Retrigger probing and announcing for all our authoritative records
         for (rr = m->ResourceRecords; rr; rr=rr->next)
+        {
             if (AuthRecord_uDNS(rr))
             {
                 ActivateUnicastRegistration(m, rr);
             if (AuthRecord_uDNS(rr))
             {
                 ActivateUnicastRegistration(m, rr);
@@ -5752,17 +6557,17 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep)
             {
                 mDNSCoreRestartRegistration(m, rr, -1);
             }
             {
                 mDNSCoreRestartRegistration(m, rr, -1);
             }
+        }
 
         // 4. Refresh NAT mappings
         // We don't want to have to assume that all hardware can necessarily keep accurate
 
         // 4. Refresh NAT mappings
         // We don't want to have to assume that all hardware can necessarily keep accurate
-        // track of passage of time while asleep, so on wake we refresh our NAT mappings
+        // track of passage of time while asleep, so on wake we refresh our NAT mappings.
         // We typically wake up with no interfaces active, so there's no need to rush to try to find our external address.
         // We typically wake up with no interfaces active, so there's no need to rush to try to find our external address.
-        // When we get a network configuration change, mDNSMacOSXNetworkChanged calls uDNS_SetupDNSConfig, which calls
-        // mDNS_SetPrimaryInterfaceInfo, which then sets m->retryGetAddr to immediately request our external address from the NAT gateway.
-        m->retryIntervalGetAddr = NATMAP_INIT_RETRY;
-        m->retryGetAddr         = m->timenow + mDNSPlatformOneSecond * 5;
-        LogInfo("mDNSCoreMachineSleep: retryGetAddr in %d %d", m->retryGetAddr - m->timenow, m->timenow);
-        RecreateNATMappings(m);
+        // But if we do get a network configuration change, mDNSMacOSXNetworkChanged will call uDNS_SetupDNSConfig, which
+        // will call mDNS_SetPrimaryInterfaceInfo, which will call RecreateNATMappings to refresh them, potentially sooner
+        // than five seconds from now.
+        LogInfo("mDNSCoreMachineSleep: recreating NAT mappings in 5 seconds");
+        RecreateNATMappings(m, mDNSPlatformOneSecond * 5);
         mDNS_Unlock(m);
     }
 }
         mDNS_Unlock(m);
     }
 }
@@ -5871,7 +6676,7 @@ spsnotready:
         // If we allow just one more second to send our goodbyes, that puts us at 27 seconds.
         m->SleepLimit = now + mDNSPlatformOneSecond * 1;
 
         // If we allow just one more second to send our goodbyes, that puts us at 27 seconds.
         m->SleepLimit = now + mDNSPlatformOneSecond * 1;
 
-        SendSleepGoodbyes(m);
+        SendSleepGoodbyes(m, mDNStrue, mDNStrue);
     }
 
 notready:
     }
 
 notready:
@@ -6181,6 +6986,40 @@ mDNSlocal CacheRecord *FindIdenticalRecordInCache(const mDNS *const m, const Res
     }
     return(rr);
 }
     }
     return(rr);
 }
+mDNSlocal void DeregisterProxyRecord(mDNS *const m, AuthRecord *const rr)
+{
+    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);
+    SetSPSProxyListChanged(m->rec.r.resrec.InterfaceID);
+}
+
+mDNSlocal void ClearKeepaliveProxyRecords(mDNS *const m, const OwnerOptData *const owner, AuthRecord *const thelist, const mDNSInterfaceID InterfaceID)
+{
+    if (m->CurrentRecord)
+        LogMsg("ClearIdenticalProxyRecords ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
+    m->CurrentRecord = thelist;
+
+    // Normally, the RDATA of the keepalive record will be different each time and hence we always
+    // clean up the keepalive record.
+    while (m->CurrentRecord)
+    {
+        AuthRecord *const rr = m->CurrentRecord;
+        if (InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&owner->HMAC, &rr->WakeUp.HMAC))
+        {
+            if (mDNS_KeepaliveRecord(&m->rec.r.resrec))
+            {
+                LogSPS("ClearKeepaliveProxyRecords: Removing %3d H-MAC %.6a I-MAC %.6a %d %d %s",
+                       m->ProxyRecords, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, owner->seq, ARDisplayString(m, rr));
+                DeregisterProxyRecord(m, rr);
+            }
+        }
+        // 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;
+    }
+}
 
 // Called from mDNSCoreReceiveUpdate when we get a sleep proxy registration request,
 // to check our lists and discard any stale duplicates of this record we already have
 
 // Called from mDNSCoreReceiveUpdate when we get a sleep proxy registration request,
 // to check our lists and discard any stale duplicates of this record we already have
@@ -6193,16 +7032,11 @@ mDNSlocal void ClearIdenticalProxyRecords(mDNS *const m, const OwnerOptData *con
     {
         AuthRecord *const rr = m->CurrentRecord;
         if (m->rec.r.resrec.InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&owner->HMAC, &rr->WakeUp.HMAC))
     {
         AuthRecord *const rr = m->CurrentRecord;
         if (m->rec.r.resrec.InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&owner->HMAC, &rr->WakeUp.HMAC))
-            // Normally, the RDATA of the keepalive record will be different each time and hence we always
-            // clean up the keepalive record.
-            if (mDNS_KeepaliveRecord(&rr->resrec) || IdenticalResourceRecord(&rr->resrec, &m->rec.r.resrec))
+            if (IdenticalResourceRecord(&rr->resrec, &m->rec.r.resrec))
             {
                 LogSPS("ClearIdenticalProxyRecords: Removing %3d H-MAC %.6a I-MAC %.6a %d %d %s",
                        m->ProxyRecords, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, owner->seq, ARDisplayString(m, rr));
             {
                 LogSPS("ClearIdenticalProxyRecords: Removing %3d H-MAC %.6a I-MAC %.6a %d %d %s",
                        m->ProxyRecords, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, owner->seq, ARDisplayString(m, rr));
-                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);
-                SetSPSProxyListChanged(m->rec.r.resrec.InterfaceID);
+                DeregisterProxyRecord(m, rr);
             }
         // 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.
             }
         // 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.
@@ -6255,7 +7089,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con
                                const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, mDNSBool QueryWasMulticast,
                                mDNSBool QueryWasLocalUnicast, DNSMessage *const response)
 {
                                const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, mDNSBool QueryWasMulticast,
                                mDNSBool QueryWasLocalUnicast, DNSMessage *const response)
 {
-    mDNSBool FromLocalSubnet    = srcaddr && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr);
+    mDNSBool FromLocalSubnet    = srcaddr && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr, mDNSNULL);
     AuthRecord   *ResponseRecords    = mDNSNULL;
     AuthRecord  **nrp                = &ResponseRecords;
 
     AuthRecord   *ResponseRecords    = mDNSNULL;
     AuthRecord  **nrp                = &ResponseRecords;
 
@@ -6272,6 +7106,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con
     mDNSu8       *responseptr        = mDNSNULL;
     AuthRecord   *rr;
     int i;
     mDNSu8       *responseptr        = mDNSNULL;
     AuthRecord   *rr;
     int i;
+    CacheRecord *McastNSEC3Records   = mDNSNULL;
 
     // ***
     // *** 1. Look in Additional Section for an OPT record
 
     // ***
     // *** 1. Look in Additional Section for an OPT record
@@ -6296,6 +7131,12 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con
         m->rec.r.resrec.RecordType = 0;     // Clear RecordType to show we're not still using it
     }
 
         m->rec.r.resrec.RecordType = 0;     // Clear RecordType to show we're not still using it
     }
 
+    // 
+    // Look in Authority Section for NSEC3 record
+    // 
+
+    mDNSParseNSEC3Records(m, query, end, InterfaceID, &McastNSEC3Records);
+
     // ***
     // *** 2. Parse Question Section and mark potential answers
     // ***
     // ***
     // *** 2. Parse Question Section and mark potential answers
     // ***
@@ -6309,6 +7150,9 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con
         ptr = getQuestion(query, ptr, end, InterfaceID, &pktq); // get the question...
         if (!ptr) goto exit;
 
         ptr = getQuestion(query, ptr, end, InterfaceID, &pktq); // get the question...
         if (!ptr) goto exit;
 
+        pktq.AnonInfo = mDNSNULL;
+        if (McastNSEC3Records)
+            InitializeAnonInfoForQuestion(m, &McastNSEC3Records, &pktq);
         // The only queries that *need* a multicast response are:
         // * Queries sent via multicast
         // * from port 5353
         // The only queries that *need* a multicast response are:
         // * Queries sent via multicast
         // * from port 5353
@@ -6319,6 +7163,12 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con
         // For other queries, we may still choose to send the occasional multicast response anyway,
         // to keep our neighbours caches warm, and for ongoing conflict detection.
         QuestionNeedsMulticastResponse = QueryWasMulticast && !LegacyQuery && !(pktq.qclass & kDNSQClass_UnicastResponse);
         // For other queries, we may still choose to send the occasional multicast response anyway,
         // to keep our neighbours caches warm, and for ongoing conflict detection.
         QuestionNeedsMulticastResponse = QueryWasMulticast && !LegacyQuery && !(pktq.qclass & kDNSQClass_UnicastResponse);
+
+        if (pktq.qclass & kDNSQClass_UnicastResponse)
+            m->mDNSStats.UnicastBitInQueries++;
+        else
+            m->mDNSStats.NormalQueries++;
+
         // Clear the UnicastResponse flag -- don't want to confuse the rest of the code that follows later
         pktq.qclass &= ~kDNSQClass_UnicastResponse;
 
         // Clear the UnicastResponse flag -- don't want to confuse the rest of the code that follows later
         pktq.qclass &= ~kDNSQClass_UnicastResponse;
 
@@ -6336,6 +7186,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con
             m->CurrentRecord = rr->next;
             if (AnyTypeRecordAnswersQuestion(&rr->resrec, &pktq) && (QueryWasMulticast || QueryWasLocalUnicast || rr->AllowRemoteQuery))
             {
             m->CurrentRecord = rr->next;
             if (AnyTypeRecordAnswersQuestion(&rr->resrec, &pktq) && (QueryWasMulticast || QueryWasLocalUnicast || rr->AllowRemoteQuery))
             {
+                m->mDNSStats.MatchingAnswersForQueries++;
                 if (RRTypeAnswersQuestionType(&rr->resrec, pktq.qtype))
                 {
                     if (rr->resrec.RecordType == kDNSRecordTypeUnique)
                 if (RRTypeAnswersQuestionType(&rr->resrec, pktq.qtype))
                 {
                     if (rr->resrec.RecordType == kDNSRecordTypeUnique)
@@ -6343,13 +7194,20 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con
                     else if (ResourceRecordIsValidAnswer(rr))
                     {
                         NumAnswersForThisQuestion++;
                     else if (ResourceRecordIsValidAnswer(rr))
                     {
                         NumAnswersForThisQuestion++;
+                        // As we have verified this question to be part of the same subset,
+                        // set the anonymous data which is needed below when walk the cache
+                        // records to see what answers we should be expecting. The cache records
+                        // may cache only the nsec3RR and not the anonymous data itself. 
+                        if (pktq.AnonInfo && rr->resrec.AnonInfo)
+                            SetAnonData(&pktq, &rr->resrec, mDNStrue);
+
                         // Note: We should check here if this is a probe-type query, and if so, generate an immediate
                         // unicast answer back to the source, because timeliness in answering probes is important.
 
                         // Notes:
                         // NR_AnswerTo pointing into query packet means "answer via immediate legacy unicast" (may *also* choose to multicast)
                         // Note: We should check here if this is a probe-type query, and if so, generate an immediate
                         // unicast answer back to the source, because timeliness in answering probes is important.
 
                         // Notes:
                         // NR_AnswerTo pointing into query packet means "answer via immediate legacy unicast" (may *also* choose to multicast)
-                        // NR_AnswerTo == (mDNSu8*)~1             means "answer via delayed unicast" (to modern querier; may promote to multicast instead)
-                        // NR_AnswerTo == (mDNSu8*)~0             means "definitely answer via multicast" (can't downgrade to unicast later)
+                        // NR_AnswerTo == NR_AnswerUnicast   means "answer via delayed unicast" (to modern querier; may promote to multicast instead)
+                        // NR_AnswerTo == NR_AnswerMulticast means "definitely answer via multicast" (can't downgrade to unicast later)
                         // If we're not multicasting this record because the kDNSQClass_UnicastResponse bit was set,
                         // but the multicast querier is not on a matching subnet (e.g. because of overlaid subnets on one link)
                         // then we'll multicast it anyway (if we unicast, the receiver will ignore it because it has an apparently non-local source)
                         // If we're not multicasting this record because the kDNSQClass_UnicastResponse bit was set,
                         // but the multicast querier is not on a matching subnet (e.g. because of overlaid subnets on one link)
                         // then we'll multicast it anyway (if we unicast, the receiver will ignore it because it has an apparently non-local source)
@@ -6360,9 +7218,9 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con
                             // This is to guard against the case where someone blasts us with queries as fast as they can.
                             if (m->timenow - (rr->LastMCTime + mDNSPlatformOneSecond) >= 0 ||
                                 (rr->LastMCInterface != mDNSInterfaceMark && rr->LastMCInterface != InterfaceID))
                             // This is to guard against the case where someone blasts us with queries as fast as they can.
                             if (m->timenow - (rr->LastMCTime + mDNSPlatformOneSecond) >= 0 ||
                                 (rr->LastMCInterface != mDNSInterfaceMark && rr->LastMCInterface != InterfaceID))
-                                rr->NR_AnswerTo = (mDNSu8*)~0;
+                                rr->NR_AnswerTo = NR_AnswerMulticast;
                         }
                         }
-                        else if (!rr->NR_AnswerTo) rr->NR_AnswerTo = LegacyQuery ? ptr : (mDNSu8*)~1;
+                        else if (!rr->NR_AnswerTo) rr->NR_AnswerTo = LegacyQuery ? ptr : NR_AnswerUnicast;
                     }
                 }
                 else if ((rr->resrec.RecordType & kDNSRecordTypeActiveUniqueMask) && ResourceRecordIsValidAnswer(rr))
                     }
                 }
                 else if ((rr->resrec.RecordType & kDNSRecordTypeActiveUniqueMask) && ResourceRecordIsValidAnswer(rr))
@@ -6385,6 +7243,8 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con
         // so use random delay on response to reduce collisions
         if (NumAnswersForThisQuestion == 0) delayresponse = mDNSPlatformOneSecond;  // Divided by 50 = 20ms
 
         // so use random delay on response to reduce collisions
         if (NumAnswersForThisQuestion == 0) delayresponse = mDNSPlatformOneSecond;  // Divided by 50 = 20ms
 
+        if (query->h.flags.b[0] & kDNSFlag0_TC)
+            m->mDNSStats.KnownAnswerMultiplePkts++;
 #if ENABLE_MULTI_PACKET_QUERY_SNOOPING
         if (QuestionNeedsMulticastResponse)
 #else
 #if ENABLE_MULTI_PACKET_QUERY_SNOOPING
         if (QuestionNeedsMulticastResponse)
 #else
@@ -6433,14 +7293,24 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con
 #if ENABLE_MULTI_PACKET_QUERY_SNOOPING
             if (!(query->h.flags.b[0] & kDNSFlag0_TC))
 #endif
 #if ENABLE_MULTI_PACKET_QUERY_SNOOPING
             if (!(query->h.flags.b[0] & kDNSFlag0_TC))
 #endif
-            for (q = m->Questions; q; q=q->next)
-                if (!q->Target.type && ActiveQuestion(q) && m->timenow - q->LastQTxTime > mDNSPlatformOneSecond / 4)
-                    if (!q->InterfaceID || q->InterfaceID == InterfaceID)
-                        if (q->NextInDQList == mDNSNULL && dqp != &q->NextInDQList)
-                            if (q->qtype == pktq.qtype &&
-                                q->qclass == pktq.qclass &&
-                                q->qnamehash == pktq.qnamehash && SameDomainName(&q->qname, &pktq.qname))
-                            { *dqp = q; dqp = &q->NextInDQList; }
+            // For anonymous question, the duplicate suppressesion should happen if the
+            // question belongs in the same group. As the group is expected to be
+            // small, we don't do the optimization for now.
+            if (!pktq.AnonInfo)
+            {
+                for (q = m->Questions; q; q=q->next)
+                    if (!q->Target.type && ActiveQuestion(q) && m->timenow - q->LastQTxTime > mDNSPlatformOneSecond / 4)
+                        if (!q->InterfaceID || q->InterfaceID == InterfaceID)
+                            if (q->NextInDQList == mDNSNULL && dqp != &q->NextInDQList)
+                                if (q->qtype == pktq.qtype &&
+                                    q->qclass == pktq.qclass &&
+                                    q->qnamehash == pktq.qnamehash && SameDomainName(&q->qname, &pktq.qname))
+                                { *dqp = q; dqp = &q->NextInDQList; }
+            }
+        }
+        if (pktq.AnonInfo)
+        {
+            FreeAnonInfo(pktq.AnonInfo);
         }
     }
 
         }
     }
 
@@ -6469,8 +7339,14 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con
         {
             // See if this Known-Answer suppresses any of our currently planned answers
             for (rr=ResponseRecords; rr; rr=rr->NextResponse)
         {
             // See if this Known-Answer suppresses any of our currently planned answers
             for (rr=ResponseRecords; rr; rr=rr->NextResponse)
+            {
                 if (MustSendRecord(rr) && ShouldSuppressKnownAnswer(&m->rec.r, rr))
                 if (MustSendRecord(rr) && ShouldSuppressKnownAnswer(&m->rec.r, rr))
-                { rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; }
+                {
+                    m->mDNSStats.KnownAnswerSuppressions++;
+                    rr->NR_AnswerTo = mDNSNULL;
+                    rr->NR_AdditionalTo = mDNSNULL;
+                }
+            }
 
             // See if this Known-Answer suppresses any previously scheduled answers (for multi-packet KA suppression)
             for (rr=m->ResourceRecords; rr; rr=rr->next)
 
             // See if this Known-Answer suppresses any previously scheduled answers (for multi-packet KA suppression)
             for (rr=m->ResourceRecords; rr; rr=rr->next)
@@ -6488,6 +7364,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con
                     }
                     if (mDNSIPv4AddressIsZero(rr->v4Requester) && mDNSIPv6AddressIsZero(rr->v6Requester))
                     {
                     }
                     if (mDNSIPv4AddressIsZero(rr->v4Requester) && mDNSIPv6AddressIsZero(rr->v6Requester))
                     {
+                        m->mDNSStats.KnownAnswerSuppressions++;
                         rr->ImmedAnswer  = mDNSNULL;
                         rr->ImmedUnicast = mDNSfalse;
     #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
                         rr->ImmedAnswer  = mDNSNULL;
                         rr->ImmedUnicast = mDNSfalse;
     #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
@@ -6555,20 +7432,42 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con
             mDNSBool SendMulticastResponse = mDNSfalse;     // Send modern multicast response
             mDNSBool SendUnicastResponse   = mDNSfalse;     // Send modern unicast response (not legacy unicast response)
 
             mDNSBool SendMulticastResponse = mDNSfalse;     // Send modern multicast response
             mDNSBool SendUnicastResponse   = mDNSfalse;     // Send modern unicast response (not legacy unicast response)
 
-            // If it's been a while since we multicast this, then send a multicast response for conflict detection, etc.
+#if !TARGET_OS_EMBEDDED
+    // always honor kDNSQClass_UnicastResponse in embedded environment to increase reliability
+    // in high multicast packet loss environments.
+
+            // If it's been one TTL/4 since we multicast this, then send a multicast response 
+            // for conflict detection, etc.
             if (m->timenow - (rr->LastMCTime + TicksTTL(rr)/4) >= 0)
             {
                 SendMulticastResponse = mDNStrue;
                 // If this record was marked for modern (delayed) unicast response, then mark it as promoted to
                 // multicast response instead (don't want to end up ALSO setting SendUnicastResponse in the check below).
                 // If this record was marked for legacy unicast response, then we mustn't change the NR_AnswerTo value.
             if (m->timenow - (rr->LastMCTime + TicksTTL(rr)/4) >= 0)
             {
                 SendMulticastResponse = mDNStrue;
                 // If this record was marked for modern (delayed) unicast response, then mark it as promoted to
                 // multicast response instead (don't want to end up ALSO setting SendUnicastResponse in the check below).
                 // If this record was marked for legacy unicast response, then we mustn't change the NR_AnswerTo value.
-                if (rr->NR_AnswerTo == (mDNSu8*)~1) rr->NR_AnswerTo = (mDNSu8*)~0;
+                if (rr->NR_AnswerTo == NR_AnswerUnicast)
+                {
+                    m->mDNSStats.UnicastDemotedToMulticast++;
+                    rr->NR_AnswerTo = NR_AnswerMulticast;
+                }
             }
             }
+#endif // !TARGET_OS_EMBEDDED
 
             // If the client insists on a multicast response, then we'd better send one
 
             // If the client insists on a multicast response, then we'd better send one
-            if      (rr->NR_AnswerTo == (mDNSu8*)~0) SendMulticastResponse = mDNStrue;
-            else if (rr->NR_AnswerTo == (mDNSu8*)~1) SendUnicastResponse   = mDNStrue;
-            else if (rr->NR_AnswerTo) SendLegacyResponse    = mDNStrue;
+            if      (rr->NR_AnswerTo == NR_AnswerMulticast)
+            {
+                m->mDNSStats.MulticastResponses++;
+                SendMulticastResponse = mDNStrue;
+            }
+            else if (rr->NR_AnswerTo == NR_AnswerUnicast)
+            {
+                m->mDNSStats.UnicastResponses++;
+                SendUnicastResponse   = mDNStrue;
+            }
+            else if (rr->NR_AnswerTo)
+            {
+                SendLegacyResponse    = mDNStrue;
+            }
+            
 
             if (SendMulticastResponse || SendUnicastResponse)
             {
 
             if (SendMulticastResponse || SendUnicastResponse)
             {
@@ -6602,7 +7501,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con
             if      (query->h.flags.b[0] & kDNSFlag0_TC) delayresponse = mDNSPlatformOneSecond * 20;            // Divided by 50 = 400ms
             else if (rr->resrec.RecordType == kDNSRecordTypeShared) delayresponse = mDNSPlatformOneSecond;      // Divided by 50 = 20ms
         }
             if      (query->h.flags.b[0] & kDNSFlag0_TC) delayresponse = mDNSPlatformOneSecond * 20;            // Divided by 50 = 400ms
             else if (rr->resrec.RecordType == kDNSRecordTypeShared) delayresponse = mDNSPlatformOneSecond;      // Divided by 50 = 20ms
         }
-        else if (rr->NR_AdditionalTo && rr->NR_AdditionalTo->NR_AnswerTo == (mDNSu8*)~0)
+        else if (rr->NR_AdditionalTo && rr->NR_AdditionalTo->NR_AnswerTo == NR_AnswerMulticast)
         {
             // Since additional records are an optimization anyway, we only ever send them on one interface at a time
             // If two clients on different interfaces do queries that invoke the same optional additional answer,
         {
             // Since additional records are an optimization anyway, we only ever send them on one interface at a time
             // If two clients on different interfaces do queries that invoke the same optional additional answer,
@@ -6697,6 +7596,7 @@ exit:
                 debugf("ProcessQuery: (Max) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s",
                        cr->UnansweredQueries, cr->MPUnansweredQ, cr->MPUnansweredKA, CRDisplayString(m, cr));
 #endif // ENABLE_MULTI_PACKET_QUERY_SNOOPING
                 debugf("ProcessQuery: (Max) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s",
                        cr->UnansweredQueries, cr->MPUnansweredQ, cr->MPUnansweredKA, CRDisplayString(m, cr));
 #endif // ENABLE_MULTI_PACKET_QUERY_SNOOPING
+            m->mDNSStats.PoofCacheDeletions++;
             mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer);
         }
 #if ENABLE_MULTI_PACKET_QUERY_SNOOPING
             mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer);
         }
 #if ENABLE_MULTI_PACKET_QUERY_SNOOPING
@@ -6744,6 +7644,12 @@ exit:
                srcaddr->type == mDNSAddrType_IPv4 ? "v4" : "v6", i);
     }
 
                srcaddr->type == mDNSAddrType_IPv4 ? "v4" : "v6", i);
     }
 
+    if (McastNSEC3Records)
+    {
+        debugf("ProcessQuery: McastNSEC3Records not used");
+        FreeNSECRecords(m, McastNSEC3Records);
+    }
+
     return(responseptr);
 }
 
     return(responseptr);
 }
 
@@ -6753,7 +7659,7 @@ mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg,
 {
     mDNSu8    *responseend = mDNSNULL;
     mDNSBool QueryWasLocalUnicast = srcaddr && dstaddr &&
 {
     mDNSu8    *responseend = mDNSNULL;
     mDNSBool QueryWasLocalUnicast = srcaddr && dstaddr &&
-                                    !mDNSAddrIsDNSMulticast(dstaddr) && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr);
+                                    !mDNSAddrIsDNSMulticast(dstaddr) && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr, mDNSNULL);
 
     if (!InterfaceID && dstaddr && mDNSAddrIsDNSMulticast(dstaddr))
     {
 
     if (!InterfaceID && dstaddr && mDNSAddrIsDNSMulticast(dstaddr))
     {
@@ -6871,6 +7777,111 @@ mDNSlocal DNSQuestion *ExpectingUnicastResponseForRecord(mDNS *const m,
     return(mDNSNULL);
 }
 
     return(mDNSNULL);
 }
 
+// Return a pointer to the primary service name, skipping subtype name if present.
+mDNSlocal const domainname *getPrimaryServiceName(const domainname *domainName)
+{
+    const domainname *primaryName = domainName;
+    const domainname *subName = SkipLeadingLabels(domainName, 1);
+    
+    if (SameDomainLabel(subName->c, (const mDNSu8 *)mDNSSubTypeLabel))
+    {
+        // skip "<sub type name>._sub" portion of name
+        primaryName = SkipLeadingLabels(domainName, 2);
+        debugf("getPrimaryServiceName: returning %##s for _sub type", primaryName);
+    }
+
+    return primaryName;
+}
+
+// This function is not called if the packet is from us, which implies that we accept all multicast packets coming from us.
+mDNSlocal mDNSBool ExpectingMulticastResponseForRecord(mDNS *const m, CacheRecord *rr, const mDNSAddr *srcaddr, mDNSBool recordAccepted,
+    CacheRecord **McastNSEC3Records)
+{
+    DNSQuestion *q;
+
+    // Accept A and AAAA if we accepted something before in the same packet as most likely related to the
+    // service records that we may have accepted.
+    if (recordAccepted && (rr->resrec.rrtype == kDNSType_A || rr->resrec.rrtype == kDNSType_AAAA))
+    {
+        LogInfo("ExpectingMulticastResponseForRecord:A:AAAA: accepting %s, from %#a due to same packet %d", CRDisplayString(m, rr), srcaddr, m->PktNum);
+        return mDNStrue;
+    }
+    for (q = m->Questions; q; q=q->next)
+    {
+        if (!q->DuplicateOf && mDNSOpaque16IsZero(q->TargetQID))
+        {
+            mDNSBool ret;
+            // 1. If a resource record answers question, cache it. This also will cache NSECs if it asserts
+            //    non-existence of q->qtype. If we have any matching NSEC3 Records for the question, send
+            //    it along with the resource record. Do it only for questions that are expecting to
+            //    discover only its peers (q->AnonInfo not NULL)
+            if (q->AnonInfo && McastNSEC3Records && !rr->resrec.AnonInfo)
+            {
+                InitializeAnonInfoForCR(m, McastNSEC3Records, rr);
+            }
+            ret = ResourceRecordAnswersQuestion(&rr->resrec, q);
+            if (ret)
+            {
+                // The record and the question belong to the same subset. Set the
+                // anonymous data in the cache record.
+                if (q->AnonInfo && rr->resrec.AnonInfo)
+                {
+                    SetAnonData(q, &rr->resrec, mDNSfalse);
+                }
+                LogInfo("ExpectingMulticastResponseForRecord: Name and Type match, accepting %s, from %#a", CRDisplayString(m, rr), srcaddr);
+                if (rr->resrec.rrtype == kDNSType_NSEC)
+                    LogInfo("ExpectingMulticastResponseForRecord: record %s, question %##s (%s)", CRDisplayString(m, rr), q->qname.c, DNSTypeName(q->qtype));
+                return mDNStrue;
+            }
+            if (rr->resrec.rrtype == kDNSType_SRV || rr->resrec.rrtype == kDNSType_TXT)
+            {
+                // Point to the service type in the record name
+                const domainname *name = SkipLeadingLabels(rr->resrec.name, 1);
+
+                // If question is for a sub type, just compare against the primary service type
+                const domainname *primaryName = getPrimaryServiceName(&q->qname);
+
+                // 2. If the SRV or TXT record matches the service name, then cache it. If the TXT or SRV record is
+                //    before the PTR record in the packet, PTR record may not be in the cache yet and hence the logic
+                //    in (3) below will fail to cache it.
+                if (q->qtype == kDNSType_PTR && name && SameDomainName(primaryName, name))
+                {
+                    LogInfo("ExpectingMulticastResponseForRecord: Accepting %s due to PTR match, question %##s from %#a, pktnum %d",
+                        CRDisplayString(m, rr), q->qname.c, srcaddr, m->PktNum);
+                    return mDNStrue;
+                }
+
+                if (name)
+                {
+                    const mDNSu32 slot = HashSlot(name);
+                    const mDNSu32 namehash = DomainNameHashValue(name);
+                    CacheGroup *cg = CacheGroupForName(m, slot, namehash, name);
+                    CacheRecord *cr;
+     
+                    // 3. Same as in (2), but look in the cache in case we don't have the PTR question.
+
+                    for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next)
+                    {
+                        if (cr->resrec.rrtype == kDNSType_PTR)
+                        {
+                            primaryName = getPrimaryServiceName(cr->resrec.name);
+
+                            if (SameDomainName(primaryName, name))
+                            {
+                                LogInfo("ExpectingMulticastResponseForRecord: accepting %s, from %#a, pktnum %d",
+                                    CRDisplayString(m, rr), srcaddr, m->PktNum);
+                                return mDNStrue;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    debugf("ExpectingMulticastResponseForRecord: discarding %s, from %#a, pktnum %d", CRDisplayString(m, rr), srcaddr, m->PktNum);
+    return(mDNSfalse);
+}
+
 // Certain data types need more space for in-memory storage than their in-packet rdlength would imply
 // Currently this applies only to rdata types containing more than one domainname,
 // or types where the domainname is not the last item in the structure.
 // Certain data types need more space for in-memory storage than their in-packet rdlength would imply
 // Currently this applies only to rdata types containing more than one domainname,
 // or types where the domainname is not the last item in the structure.
@@ -6902,8 +7913,18 @@ mDNSexport CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, C
     {
         RData *saveptr = rr->resrec.rdata;      // Save the rr->resrec.rdata pointer
         *rr = m->rec.r;                         // Block copy the CacheRecord object
     {
         RData *saveptr = rr->resrec.rdata;      // Save the rr->resrec.rdata pointer
         *rr = m->rec.r;                         // Block copy the CacheRecord object
-        rr->resrec.rdata  = saveptr;                // Restore rr->resrec.rdata after the structure assignment
+        rr->resrec.rdata  = saveptr;            // Restore rr->resrec.rdata after the structure assignment
         rr->resrec.name   = cg->name;           // And set rr->resrec.name to point into our CacheGroup header
         rr->resrec.name   = cg->name;           // And set rr->resrec.name to point into our CacheGroup header
+
+        // We need to add the anonymous info before we call CacheRecordAdd so that
+        // if it finds a matching question with this record, it bumps up the counters like
+        // CurrentAnswers etc. Otherwise, when a cache entry gets removed, CacheRecordRmv
+        // will complain.
+        if (m->rec.r.resrec.AnonInfo)
+        {
+            rr->resrec.AnonInfo = m->rec.r.resrec.AnonInfo;
+            m->rec.r.resrec.AnonInfo = mDNSNULL;
+        }
         rr->DelayDelivery = delay;
 
         // If this is an oversized record with external storage allocated, copy rdata to external storage
         rr->DelayDelivery = delay;
 
         // If this is an oversized record with external storage allocated, copy rdata to external storage
@@ -6916,10 +7937,18 @@ mDNSexport CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, C
 
         rr->next = mDNSNULL;                    // Clear 'next' pointer
         rr->nsec = mDNSNULL;
 
         rr->next = mDNSNULL;                    // Clear 'next' pointer
         rr->nsec = mDNSNULL;
+        rr->soa  = mDNSNULL;
 
         if (sourceAddress)
             rr->sourceAddress = *sourceAddress;
 
 
         if (sourceAddress)
             rr->sourceAddress = *sourceAddress;
 
+        if (!rr->resrec.InterfaceID)
+        {
+            m->rrcache_totalused_unicast += rr->resrec.rdlength;
+            if (DNSSECRecordType(rr->resrec.rrtype))
+                BumpDNSSECStats(m, kStatsActionIncrement, kStatsTypeMemoryUsage, rr->resrec.rdlength);
+        }
+
         if (Add)
         {
             *(cg->rrcache_tail) = rr;               // Append this record to tail of cache slot list
         if (Add)
         {
             *(cg->rrcache_tail) = rr;               // Append this record to tail of cache slot list
@@ -6988,15 +8017,7 @@ mDNSlocal mDNSu32 GetEffectiveTTL(const uDNS_LLQType LLQType, mDNSu32 ttl)
         // also do this check here to make sure we can't get overflow below when we add a quarter to the TTL
         if (ttl > 0x60000000UL / mDNSPlatformOneSecond) ttl = 0x60000000UL / mDNSPlatformOneSecond;
 
         // also do this check here to make sure we can't get overflow below when we add a quarter to the TTL
         if (ttl > 0x60000000UL / mDNSPlatformOneSecond) ttl = 0x60000000UL / mDNSPlatformOneSecond;
 
-        // Adjustment factor to avoid race condition:
-        // Suppose real record as TTL of 3600, and our local caching server has held it for 3500 seconds, so it returns an aged TTL of 100.
-        // If we do our normal refresh at 80% of the TTL, our local caching server will return 20 seconds, so we'll do another
-        // 80% refresh after 16 seconds, and then the server will return 4 seconds, and so on, in the fashion of Zeno's paradox.
-        // To avoid this, we extend the record's effective TTL to give it a little extra grace period.
-        // We adjust the 100 second TTL to 126. This means that when we do our 80% query at 101 seconds,
-        // the cached copy at our local caching server will already have expired, so the server will be forced
-        // to fetch a fresh copy from the authoritative server, and then return a fresh record with the full TTL of 3600 seconds.
-        ttl += ttl/4 + 2;
+        ttl = RRAdjustTTL(ttl);
 
         // For mDNS, TTL zero means "delete this record"
         // For uDNS, TTL zero means: this data is true at this moment, but don't cache it.
 
         // For mDNS, TTL zero means "delete this record"
         // For uDNS, TTL zero means: this data is true at this moment, but don't cache it.
@@ -7073,9 +8094,15 @@ mDNSlocal mDNSBool IsResponseAcceptable(mDNS *const m, const CacheRecord *crlist
                         DNSTypeName(q->qtype));
                 return mDNStrue;
             }
                         DNSTypeName(q->qtype));
                 return mDNStrue;
             }
-            else if (typeCovered == kDNSType_NSEC)
+            else if (typeCovered == kDNSType_NSEC || typeCovered == kDNSType_NSEC3)
+            {
+                LogInfo("IsResponseAcceptable: Accepting RRSIG %s matches %s type (nseclist = 1)", CRDisplayString(m, newcr), DNSTypeName(typeCovered));
+                *nseclist = mDNStrue;
+                return mDNStrue;
+            }
+            else if (typeCovered == kDNSType_SOA)
             {
             {
-                LogInfo("IsResponseAcceptable: Accepting RRSIG %s matches NSEC type (nseclist = 1)", CRDisplayString(m, newcr));
+                LogInfo("IsResponseAcceptable: Accepting RRSIG %s matches SOA type (nseclist = 1)", CRDisplayString(m, newcr));
                 *nseclist = mDNStrue;
                 return mDNStrue;
             }
                 *nseclist = mDNStrue;
                 return mDNStrue;
             }
@@ -7092,6 +8119,18 @@ mDNSlocal mDNSBool IsResponseAcceptable(mDNS *const m, const CacheRecord *crlist
             *nseclist = mDNStrue;
             return mDNStrue;
         }
             *nseclist = mDNStrue;
             return mDNStrue;
         }
+        if (rr->rrtype == kDNSType_SOA)
+        {
+            LogInfo("IsResponseAcceptable: Accepting SOA %s (nseclist = 1)", CRDisplayString(m, newcr));
+            *nseclist = mDNStrue;
+            return mDNStrue;
+        }
+        else if (rr->rrtype == kDNSType_NSEC3)
+        {
+            LogInfo("IsResponseAcceptable: Accepting NSEC3 %s (nseclist = 1)", CRDisplayString(m, newcr));
+            *nseclist = mDNStrue;
+            return mDNStrue;
+        }
     }
     return mDNSfalse;
 }
     }
     return mDNSfalse;
 }
@@ -7107,11 +8146,58 @@ mDNSlocal void FreeNSECRecords(mDNS *const m, CacheRecord *NSECRecords)
     }
 }
 
     }
 }
 
+// If we received zero DNSSEC records even when the DO/EDNS0 bit was set, we need to provide this
+// information to ValidatingResponse question to indicate the DNSSEC status to the application
+mDNSlocal void mDNSCoreReceiveNoDNSSECAnswers(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end, const mDNSAddr *dstaddr,
+    mDNSIPPort dstport, const mDNSInterfaceID InterfaceID)
+{
+    int i;
+    const mDNSu8 *ptr = response->data;
+
+    for (i = 0; i < response->h.numQuestions && ptr && ptr < end; i++)
+    {
+        DNSQuestion pktq;
+        DNSQuestion *qptr = mDNSNULL;
+        ptr = getQuestion(response, ptr, end, InterfaceID, &pktq);
+        if (ptr && (qptr = ExpectingUnicastResponseForQuestion(m, dstport, response->h.id, &pktq, !dstaddr)) &&
+            qptr->ValidatingResponse)
+        {
+            DNSQuestion *next, *q;
+
+            if (qptr->DuplicateOf)
+                LogMsg("mDNSCoreReceiveNoDNSSECAnswers: ERROR!! qptr %##s (%s) Duplicate question matching response", qptr->qname.c, DNSTypeName(qptr->qtype));
+
+            // Be careful to call the callback for duplicate questions first and then the original
+            // question. If we called the callback on the original question, it could stop and 
+            // a duplicate question would become the original question.
+            mDNS_DropLockBeforeCallback();      // Allow client (and us) to legally make mDNS API calls
+            for (q = qptr->next ; q && q != m->NewQuestions; q = next)
+            {
+                next = q->next;
+                if (q->DuplicateOf == qptr)
+                {
+                    if (q->ValidatingResponse)
+                        LogInfo("mDNSCoreReceiveNoDNSSECAnswers: qptr %##s (%s) Duplicate question found", q->qname.c, DNSTypeName(q->qtype));
+                    else
+                        LogMsg("mDNSCoreReceiveNoDNSSECAnswers: ERROR!! qptr %##s (%s) Duplicate question not ValidatingResponse", q->qname.c, DNSTypeName(q->qtype));
+                    if (q->QuestionCallback)
+                        q->QuestionCallback(m, q, mDNSNULL, QC_nodnssec);
+                }
+            }
+            if (qptr->QuestionCallback)
+                qptr->QuestionCallback(m, qptr, mDNSNULL, QC_nodnssec);
+            mDNS_ReclaimLockAfterCallback();    // Decrement mDNS_reentrancy to block mDNS API calls again
+        }
+    }
+}
+
 mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end, const mDNSAddr *dstaddr,
                                                mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, uDNS_LLQType LLQType, mDNSu8 rcode, CacheRecord *NSECRecords)
 {
     int i;
     const mDNSu8 *ptr   = response->data;
 mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end, const mDNSAddr *dstaddr,
                                                mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, uDNS_LLQType LLQType, mDNSu8 rcode, CacheRecord *NSECRecords)
 {
     int i;
     const mDNSu8 *ptr   = response->data;
+    CacheRecord *SOARecord = mDNSNULL;
+
     for (i = 0; i < response->h.numQuestions && ptr && ptr < end; i++)
     {
         DNSQuestion q;
     for (i = 0; i < response->h.numQuestions && ptr && ptr < end; i++)
     {
         DNSQuestion q;
@@ -7152,10 +8238,18 @@ mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage *
                 {
                     LogInfo("mDNSCoreReceiveNoUnicastAnswers: Generate negative response for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
                     m->CurrentQuestion = qptr;
                 {
                     LogInfo("mDNSCoreReceiveNoUnicastAnswers: Generate negative response for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
                     m->CurrentQuestion = qptr;
-                    GenerateNegativeResponse(m);
+                    // We are not creating a cache record in this case, we need to pass back
+                    // the error we got so that the proxy code can return the right one to
+                    // the application
+                    if (qptr->ProxyQuestion)
+                        qptr->responseFlags = response->h.flags;
+                    GenerateNegativeResponse(m, QC_forceresponse);
                     m->CurrentQuestion = mDNSNULL;
                 }
                     m->CurrentQuestion = mDNSNULL;
                 }
-                else LogInfo("mDNSCoreReceiveNoUnicastAnswers: Skipping check to see if we need to generate a negative cache entry for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
+                else
+                {
+                    LogInfo("mDNSCoreReceiveNoUnicastAnswers: Skipping check and not creating a negative cache entry for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
+                }
             }
             else
             {
             }
             else
             {
@@ -7182,6 +8276,8 @@ mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage *
                         ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &m->rec);
                         if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_SOA)
                         {
                         ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &m->rec);
                         if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_SOA)
                         {
+                            const mDNSu32 s = HashSlot(m->rec.r.resrec.name);
+                            CacheGroup *cgSOA = CacheGroupForRecord(m, s, &m->rec.r.resrec);
                             const rdataSOA *const soa = (const rdataSOA *)m->rec.r.resrec.rdata->u.data;
                             mDNSu32 ttl_s = soa->min;
                             // We use the lesser of the SOA.MIN field and the SOA record's TTL, *except*
                             const rdataSOA *const soa = (const rdataSOA *)m->rec.r.resrec.rdata->u.data;
                             mDNSu32 ttl_s = soa->min;
                             // We use the lesser of the SOA.MIN field and the SOA record's TTL, *except*
@@ -7191,11 +8287,18 @@ mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage *
                                 ttl_s = m->rec.r.resrec.rroriginalttl;
                             if (negttl < ttl_s) negttl = ttl_s;
 
                                 ttl_s = m->rec.r.resrec.rroriginalttl;
                             if (negttl < ttl_s) negttl = ttl_s;
 
+                            // Create the SOA record as we may have to return this to the questions
+                            // that we are acting as a proxy for currently or in the future.
+                            SOARecord = CreateNewCacheEntry(m, s, cgSOA, 1, mDNSfalse, mDNSNULL);
+
                             // Special check for SOA queries: If we queried for a.b.c.d.com, and got no answer,
                             // with an Authority Section SOA record for d.com, then this is a hint that the authority
                             // is d.com, and consequently SOA records b.c.d.com and c.d.com don't exist either.
                             // To do this we set the repeat count so the while loop below will make a series of negative cache entries for us
                             // Special check for SOA queries: If we queried for a.b.c.d.com, and got no answer,
                             // with an Authority Section SOA record for d.com, then this is a hint that the authority
                             // is d.com, and consequently SOA records b.c.d.com and c.d.com don't exist either.
                             // To do this we set the repeat count so the while loop below will make a series of negative cache entries for us
-                            if (q.qtype == kDNSType_SOA)
+                            //
+                            // For ProxyQuestions, we don't do this as we need to create additional SOA records to cache them
+                            // along with the negative cache record. For simplicity, we don't create the additional records.
+                            if (!qptr->ProxyQuestion && q.qtype == kDNSType_SOA)
                             {
                                 int qcount = CountLabels(&q.qname);
                                 int scount = CountLabels(m->rec.r.resrec.name);
                             {
                                 int qcount = CountLabels(&q.qname);
                                 int scount = CountLabels(m->rec.r.resrec.name);
@@ -7234,44 +8337,90 @@ mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage *
                         // restarted (setting it to InitialQuestionInterval) for other reasons e.g., wakeup,
                         // we should reset its question interval here to MaxQuestionInterval.
                         ResetQuestionState(m, qptr);
                         // restarted (setting it to InitialQuestionInterval) for other reasons e.g., wakeup,
                         // we should reset its question interval here to MaxQuestionInterval.
                         ResetQuestionState(m, qptr);
+                        if (DNSSECQuestion(qptr))
+                            neg->CRDNSSECQuestion = 1;
                         // Update the NSEC records again.
                         // TBD: Need to purge and revalidate if the cached NSECS and the new set are not same.
                         if (NSECRecords)
                         {
                             if (!AddNSECSForCacheRecord(m, NSECRecords, neg, rcode))
                             {
                         // Update the NSEC records again.
                         // TBD: Need to purge and revalidate if the cached NSECS and the new set are not same.
                         if (NSECRecords)
                         {
                             if (!AddNSECSForCacheRecord(m, NSECRecords, neg, rcode))
                             {
-                                LogMsg("mDNSCoreReceiveNoUnicastAnswers: AddNSECSForCacheRecord failed to add NSEC for negcr %s during refresh", CRDisplayString(m, neg));
+                                // We might just have an SOA record for zones that are not signed and hence don't log
+                                // this as an error
+                                LogInfo("mDNSCoreReceiveNoUnicastAnswers: AddNSECSForCacheRecord failed to add NSEC for negcr %s during refresh", CRDisplayString(m, neg));
                                 FreeNSECRecords(m, NSECRecords);
                                 FreeNSECRecords(m, NSECRecords);
+                                neg->CRDNSSECQuestion = 0;
                             }
                             NSECRecords = mDNSNULL;
                         }
                             }
                             NSECRecords = mDNSNULL;
                         }
+                        if (SOARecord)
+                        {
+                            if (neg->soa)
+                                ReleaseCacheRecord(m, neg->soa);
+                            neg->soa = SOARecord;
+                            SOARecord = mDNSNULL;
+                        }
                     }
                     else while (1)
                         {
                     }
                     else while (1)
                         {
+                            CacheRecord *negcr;
                             debugf("mDNSCoreReceiveNoUnicastAnswers making negative cache entry TTL %d for %##s (%s)", negttl, name->c, DNSTypeName(q.qtype));
                             MakeNegativeCacheRecord(m, &m->rec.r, name, hash, q.qtype, q.qclass, negttl, mDNSInterface_Any, qptr->qDNSServer);
                             debugf("mDNSCoreReceiveNoUnicastAnswers making negative cache entry TTL %d for %##s (%s)", negttl, name->c, DNSTypeName(q.qtype));
                             MakeNegativeCacheRecord(m, &m->rec.r, name, hash, q.qtype, q.qclass, negttl, mDNSInterface_Any, qptr->qDNSServer);
+                            m->rec.r.responseFlags = response->h.flags;
+                            // We create SOA records above which might create new cache groups. Earlier
+                            // in the function we looked up the cache group for the name and it could have
+                            // been NULL. If we pass NULL cg to new cache entries that we create below,
+                            // it will create additional cache groups for the same name. To avoid that,
+                            // look up the cache group again to re-initialize cg again.
+                            cg = CacheGroupForName(m, slot, hash, name);
                             if (NSECRecords && DNSSECQuestion(qptr))
                             {
                             if (NSECRecords && DNSSECQuestion(qptr))
                             {
-                                CacheRecord *negcr;
                                 // Create the cache entry with delay and then add the NSEC records
                                 // to it and add it immediately.
                                 negcr = CreateNewCacheEntry(m, slot, cg, 1, mDNStrue, mDNSNULL);
                                 // Create the cache entry with delay and then add the NSEC records
                                 // to it and add it immediately.
                                 negcr = CreateNewCacheEntry(m, slot, cg, 1, mDNStrue, mDNSNULL);
-                                if (!AddNSECSForCacheRecord(m, NSECRecords, negcr, rcode))
+                                if (negcr)
                                 {
                                 {
-                                    LogMsg("mDNSCoreReceiveNoUnicastAnswers: AddNSECSForCacheRecord failed to add NSEC for negcr %s", CRDisplayString(m, negcr));
-                                    FreeNSECRecords(m, NSECRecords);
+                                    negcr->CRDNSSECQuestion = 0;
+                                    if (!AddNSECSForCacheRecord(m, NSECRecords, negcr, rcode))
+                                    {
+                                        LogInfo("mDNSCoreReceiveNoUnicastAnswers: AddNSECSForCacheRecord failed to add NSEC for negcr %s",
+                                            CRDisplayString(m, negcr));
+                                        FreeNSECRecords(m, NSECRecords);
+                                    }
+                                    else
+                                    {
+                                        negcr->CRDNSSECQuestion = 1;
+                                        LogInfo("mDNSCoreReceiveNoUnicastAnswers: AddNSECSForCacheRecord added neg NSEC for %s", CRDisplayString(m, negcr));
+                                    }
+                                    NSECRecords = mDNSNULL;
+                                    negcr->DelayDelivery = 0;
+                                    CacheRecordDeferredAdd(m, negcr);
                                 }
                                 }
-                                else LogInfo("mDNSCoreReceiveResponse: AddNSECSForCacheRecord added neg NSEC for %s", CRDisplayString(m, negcr));
-                                NSECRecords = mDNSNULL;
-                                negcr->DelayDelivery = 0;
-                                CacheRecordDeferredAdd(m, negcr);
                                 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
                                 break;
                             }
                             else
                             {
                                 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
                                 break;
                             }
                             else
                             {
-                                CreateNewCacheEntry(m, slot, cg, 0, mDNStrue, mDNSNULL); // We never need any delivery delay for these generated negative cache records
+                                // Need to add with a delay so that we can tag the SOA record
+                                negcr = CreateNewCacheEntry(m, slot, cg, 1, mDNStrue, mDNSNULL);
+                                if (negcr)
+                                {
+                                    negcr->CRDNSSECQuestion = 0;
+                                    if (DNSSECQuestion(qptr))
+                                        negcr->CRDNSSECQuestion = 1;
+                                    negcr->DelayDelivery = 0;
+
+                                    if (SOARecord)
+                                    {
+                                        if (negcr->soa)
+                                            ReleaseCacheRecord(m, negcr->soa);
+                                        negcr->soa = SOARecord;
+                                        SOARecord = mDNSNULL;
+                                    }
+                                    CacheRecordDeferredAdd(m, negcr);
+                                }
                             }
                             }
+                            m->rec.r.responseFlags = zeroID;
                             m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
                             if (!repeat) break;
                             repeat--;
                             m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
                             if (!repeat) break;
                             repeat--;
@@ -7285,6 +8434,7 @@ mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage *
         }
     }
     if (NSECRecords) { LogInfo("mDNSCoreReceiveNoUnicastAnswers: NSECRecords not used"); FreeNSECRecords(m, NSECRecords); }
         }
     }
     if (NSECRecords) { LogInfo("mDNSCoreReceiveNoUnicastAnswers: NSECRecords not used"); FreeNSECRecords(m, NSECRecords); }
+    if (SOARecord)   { LogInfo("mDNSCoreReceiveNoUnicastAnswers: SOARecord not used"); ReleaseCacheRecord(m, SOARecord); }
 }
 
 mDNSlocal void mDNSCorePrintStoredProxyRecords(mDNS *const m)
 }
 
 mDNSlocal void mDNSCorePrintStoredProxyRecords(mDNS *const m)
@@ -7313,20 +8463,254 @@ mDNSlocal mDNSBool mDNSCoreRegisteredProxyRecord(mDNS *const m, AuthRecord *rr)
     return mDNSfalse;
 }
 
     return mDNSfalse;
 }
 
-// Note: mDNSCoreReceiveResponse calls mDNS_Deregister_internal which can call a user callback, which may change
-// the record list and/or question list.
-// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
-// InterfaceID non-NULL tells us the interface this multicast response was received on
-// InterfaceID NULL tells us this was a unicast response
-// dstaddr NULL tells us we received this over an outgoing TCP connection we made
-mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m,
-                                       const DNSMessage *const response, const mDNSu8 *end,
-                                       const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport,
-                                       const mDNSInterfaceID InterfaceID)
+mDNSlocal CacheRecord* mDNSCoreReceiveCacheCheck(mDNS *const m, const DNSMessage *const response, uDNS_LLQType LLQType,
+    const mDNSu32 slot, CacheGroup *cg, DNSQuestion *unicastQuestion, CacheRecord ***cfp, CacheRecord **NSECCachePtr,
+    mDNSInterfaceID InterfaceID)
 {
 {
-    int i;
+    CacheRecord *rr;
+    CacheRecord **cflocal = *cfp;
+
+    for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
+    {
+        mDNSBool match;
+        // Resource record received via unicast, the resGroupID should match ?
+        if (!InterfaceID)
+        {
+            mDNSu16 id1 = (rr->resrec.rDNSServer ? rr->resrec.rDNSServer->resGroupID : 0);
+            mDNSu16 id2 = (m->rec.r.resrec.rDNSServer ? m->rec.r.resrec.rDNSServer->resGroupID : 0);
+            match = (id1 == id2);
+        }
+        else
+            match = (rr->resrec.InterfaceID == InterfaceID);
+        // If we found this exact resource record, refresh its TTL
+        if (match && IdenticalSameNameRecord(&m->rec.r.resrec, &rr->resrec))
+        {
+            if (m->rec.r.resrec.rdlength > InlineCacheRDSize)
+                verbosedebugf("mDNSCoreReceiveCacheCheck: Found record size %5d interface %p already in cache: %s",
+                              m->rec.r.resrec.rdlength, InterfaceID, CRDisplayString(m, &m->rec.r));
+
+            if (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask)
+            {
+                // If this packet record has the kDNSClass_UniqueRRSet flag set, then add it to our cache flushing list
+                if (rr->NextInCFList == mDNSNULL && *cfp != &rr->NextInCFList && LLQType != uDNS_LLQ_Events)
+                {
+                    *cflocal = rr;
+                    cflocal = &rr->NextInCFList;
+                    *cflocal = (CacheRecord*)1;
+                    *cfp = &rr->NextInCFList;
+                }
+
+                // If this packet record is marked unique, and our previous cached copy was not, then fix it
+                if (!(rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask))
+                {
+                    DNSQuestion *q;
+                    for (q = m->Questions; q; q=q->next)
+                    {
+                        if (ResourceRecordAnswersQuestion(&rr->resrec, q))
+                            q->UniqueAnswers++;
+                    }
+                    rr->resrec.RecordType = m->rec.r.resrec.RecordType;
+                }
+            }
+
+            if (!SameRDataBody(&m->rec.r.resrec, &rr->resrec.rdata->u, SameDomainNameCS))
+            {
+                // If the rdata of the packet record differs in name capitalization from the record in our cache
+                // then mDNSPlatformMemSame will detect this. In this case, throw the old record away, so that clients get
+                // a 'remove' event for the record with the old capitalization, and then an 'add' event for the new one.
+                // <rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing
+                rr->resrec.rroriginalttl = 0;
+                rr->TimeRcvd = m->timenow;
+                rr->UnansweredQueries = MaxUnansweredQueries;
+                SetNextCacheCheckTimeForRecord(m, rr);
+                LogInfo("mDNSCoreReceiveCacheCheck: Discarding due to domainname case change old: %s", CRDisplayString(m, rr));
+                LogInfo("mDNSCoreReceiveCacheCheck: Discarding due to domainname case change new: %s", CRDisplayString(m, &m->rec.r));
+                LogInfo("mDNSCoreReceiveCacheCheck: Discarding due to domainname case change in %d slot %3d in %d %d",
+                        NextCacheCheckEvent(rr) - m->timenow, slot, m->rrcache_nextcheck[slot] - m->timenow, m->NextCacheCheck - m->timenow);
+                // DO NOT break out here -- we want to continue as if we never found it
+            }
+            else if (!IdenticalAnonInfo(m->rec.r.resrec.AnonInfo, rr->resrec.AnonInfo))
+            {
+                // If the NSEC3 record changed, a few possibilities
+                //
+                // 1) the peer reinitialized e.g., after network change and still part of the
+                //    same set.
+                // 2) the peer went to a different set but we did not see the goodbyes. If we just
+                //    update the nsec3 record, it would be incorrect. Flush the cache so that we
+                //    can deliver a RMV followed by ADD.
+                // 3) if the peer is ourselves and we see the goodbye when moving to a different set
+                //    and so we flush the cache and create a new cache record with the new set information.
+                //    Now we move back to the original set. In this case, we can't just update the
+                //    NSEC3 record alone. We need to flush so that we can deliver an RMV followed by ADD
+                //    when we create the new cache entry.
+                //
+                // Note: For case (1), we could avoid flushing the cache but we can't tell the difference
+                // from the other cases.
+                rr->resrec.rroriginalttl = 0;
+                rr->TimeRcvd = m->timenow;
+                rr->UnansweredQueries = MaxUnansweredQueries;
+                SetNextCacheCheckTimeForRecord(m, rr);
+                LogInfo("mDNSCoreReceiveCacheCheck: AnonInfo changed for %s", CRDisplayString(m, rr));
+                // DO NOT break out here -- we want to continue as if we never found it. When we return
+                // from this function, we will create a new cache entry with the new NSEC3 record
+            }
+            else if (m->rec.r.resrec.rroriginalttl > 0)
+            {
+                DNSQuestion *q;
+
+                m->mDNSStats.CacheRefreshed++;
+
+                if (rr->resrec.rroriginalttl == 0) debugf("uDNS rescuing %s", CRDisplayString(m, rr));
+                RefreshCacheRecord(m, rr, m->rec.r.resrec.rroriginalttl);
+                rr->responseFlags = response->h.flags;
+
+                // If we may have NSEC records returned with the answer (which we don't know yet as it
+                // has not been processed), we need to cache them along with the first cache
+                // record in the list that answers the question so that it can be used for validation
+                // later. The "type" check below is to make sure that we cache on the cache record
+                // that would answer the question. It is possible that we might cache additional things
+                // e.g., MX question might cache A records also, and we want to cache the NSEC on
+                // the record that answers the question.
+                if (response->h.numAnswers && unicastQuestion && unicastQuestion->qtype == rr->resrec.rrtype
+                    && !(*NSECCachePtr))
+                {
+                    LogInfo("mDNSCoreReceiveCacheCheck: rescuing RR %s", CRDisplayString(m, rr));
+                    *NSECCachePtr = rr;
+                }
+                // We have to reset the question interval to MaxQuestionInterval so that we don't keep
+                // polling the network once we get a valid response back. For the first time when a new
+                // cache entry is created, AnswerCurrentQuestionWithResourceRecord does that.
+                // Subsequently, if we reissue questions from within the mDNSResponder e.g., DNS server
+                // configuration changed, without flushing the cache, we reset the question interval here.
+                // Currently, we do this for for both multicast and unicast questions as long as the record
+                // type is unique. For unicast, resource record is always unique and for multicast it is
+                // true for records like A etc. but not for PTR.
+                if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)
+                {
+                    for (q = m->Questions; q; q=q->next)
+                    {
+                        if (!q->DuplicateOf && !q->LongLived &&
+                            ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q))
+                        {
+                            ResetQuestionState(m, q);
+                            debugf("mDNSCoreReceiveCacheCheck: Set MaxQuestionInterval for %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype));
+                            break;      // Why break here? Aren't there other questions we might want to look at?-- SC July 2010
+                        }
+                    }
+                }
+                break;
+            }
+            else
+            {
+            
+                // If the packet TTL is zero, that means we're deleting this record.
+                // To give other hosts on the network a chance to protest, we push the deletion
+                // out one second into the future. Also, we set UnansweredQueries to MaxUnansweredQueries.
+                // Otherwise, we'll do final queries for this record at 80% and 90% of its apparent
+                // lifetime (800ms and 900ms from now) which is a pointless waste of network bandwidth.
+                // If record's current expiry time is more than a second from now, we set it to expire in one second.
+                // If the record is already going to expire in less than one second anyway, we leave it alone --
+                // we don't want to let the goodbye packet *extend* the record's lifetime in our cache.
+                debugf("DE for %s", CRDisplayString(m, rr));
+                if (RRExpireTime(rr) - m->timenow > mDNSPlatformOneSecond)
+                {
+                    rr->resrec.rroriginalttl = 1;
+                    rr->TimeRcvd = m->timenow;
+                    rr->UnansweredQueries = MaxUnansweredQueries;
+                    SetNextCacheCheckTimeForRecord(m, rr);
+                }
+                break;
+            }
+        }
+    }
+    return rr;
+}
+
+mDNSlocal void mDNSParseNSEC3Records(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end,
+    const mDNSInterfaceID InterfaceID, CacheRecord **NSEC3Records)
+{
+    const mDNSu8 *ptr   = response->data;
+    CacheRecord *rr;
+    int i;
+
+    if (!response->h.numAuthorities)
+        return;
+    ptr = LocateAuthorities(response, end);
+    if (!ptr)
+    {
+        LogInfo("mDNSParseNSEC3Records: ERROR can't locate authorities");
+        return;
+    }
+    for (i = 0; i < response->h.numAuthorities && ptr && ptr < end; i++)
+    {
+        mDNSu32 slot;
+        CacheGroup *cg;
+
+        ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &m->rec);
+        if (!ptr || m->rec.r.resrec.RecordType == kDNSRecordTypePacketNegative || m->rec.r.resrec.rrtype != kDNSType_NSEC3)
+        {
+            debugf("mDNSParseNSEC3Records: ptr %p, Record %s, ignoring", ptr, CRDisplayString(m, &m->rec.r));
+            m->rec.r.resrec.RecordType = 0;
+            continue;
+        }
+        slot = HashSlot(m->rec.r.resrec.name);
+        cg = CacheGroupForRecord(m, slot, &m->rec.r.resrec);
+        // Create the cache entry but don't add it to the cache it. We need
+        // to cache this along with the main cache record.
+        rr = CreateNewCacheEntry(m, slot, cg, 0, mDNSfalse, mDNSNULL);
+        if (rr)
+        {
+            debugf("mDNSParseNSEC3Records: %s", CRDisplayString(m, rr));
+            *NSEC3Records = rr;
+            NSEC3Records = &rr->next;
+        }
+        m->rec.r.resrec.RecordType = 0;     // Clear RecordType to show we're not still using it
+    }
+}
+
+mDNSlocal void mDNSCoreResetRecord(mDNS *const m)
+{
+    m->rec.r.resrec.RecordType = 0;     // Clear RecordType to show we're not still using it
+    if (m->rec.r.resrec.AnonInfo)
+    {
+        FreeAnonInfo(m->rec.r.resrec.AnonInfo); 
+        m->rec.r.resrec.AnonInfo = mDNSNULL;
+    }
+}
+
+#define DEVICE_INFO_RECORD_LABELS 4
+
+// Determine if the record is an instance of _device-info._tcp.local.
+mDNSlocal mDNSBool IsDeviceInfoRecord(const domainname *d)
+{
+    const domainname *afterInstance;
+
+    if (CountLabels(d) != DEVICE_INFO_RECORD_LABELS)
+        return mDNSfalse;
+
+    // skip the instance name
+    afterInstance = SkipLeadingLabels(d, 1);
+    if (SameDomainName(afterInstance, &LocalDeviceInfoName))
+        return mDNStrue;
+
+    return mDNSfalse;
+}
+
+// Note: mDNSCoreReceiveResponse calls mDNS_Deregister_internal which can call a user callback, which may change
+// the record list and/or question list.
+// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
+// InterfaceID non-NULL tells us the interface this multicast response was received on
+// InterfaceID NULL tells us this was a unicast response
+// dstaddr NULL tells us we received this over an outgoing TCP connection we made
+mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m,
+                                       const DNSMessage *const response, const mDNSu8 *end,
+                                       const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport,
+                                       const mDNSInterfaceID InterfaceID)
+{
+    int i;
+    mDNSBool myself;
     mDNSBool ResponseMCast    = dstaddr && mDNSAddrIsDNSMulticast(dstaddr);
     mDNSBool ResponseMCast    = dstaddr && mDNSAddrIsDNSMulticast(dstaddr);
-    mDNSBool ResponseSrcLocal = !srcaddr || mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr);
+    mDNSBool ResponseSrcLocal = !srcaddr || mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr, &myself);
     DNSQuestion *llqMatch = mDNSNULL;
     DNSQuestion *unicastQuestion = mDNSNULL;
     uDNS_LLQType LLQType      = uDNS_recvLLQResponse(m, response, end, srcaddr, srcport, &llqMatch);
     DNSQuestion *llqMatch = mDNSNULL;
     DNSQuestion *unicastQuestion = mDNSNULL;
     uDNS_LLQType LLQType      = uDNS_recvLLQResponse(m, response, end, srcaddr, srcport, &llqMatch);
@@ -7339,8 +8723,13 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m,
     CacheRecord *NSECRecords = mDNSNULL;
     CacheRecord *NSECCachePtr = mDNSNULL;
     CacheRecord **nsecp = &NSECRecords;
     CacheRecord *NSECRecords = mDNSNULL;
     CacheRecord *NSECCachePtr = mDNSNULL;
     CacheRecord **nsecp = &NSECRecords;
+    CacheRecord *McastNSEC3Records = mDNSNULL;
     mDNSBool nseclist;
     mDNSu8 rcode = '\0';
     mDNSBool nseclist;
     mDNSu8 rcode = '\0';
+    mDNSBool rrsigsCreated = mDNSfalse;
+    mDNSBool DNSSECQuestion = mDNSfalse;
+    mDNSBool recordAccepted = mDNSfalse;
+    NetworkInterfaceInfo *llintf = FirstIPv4LLInterfaceForID(m, InterfaceID);
 
     // All records in a DNS response packet are treated as equally valid statements of truth. If we want
     // to guard against spoof responses, then the only credible protection against that is cryptographic
 
     // All records in a DNS response packet are treated as equally valid statements of truth. If we want
     // to guard against spoof responses, then the only credible protection against that is cryptographic
@@ -7411,7 +8800,19 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m,
                     // decisions later on in this function
                     const mDNSu32 slot = HashSlot(&q.qname);
                     CacheGroup *cg = CacheGroupForName(m, slot, q.qnamehash, &q.qname);
                     // decisions later on in this function
                     const mDNSu32 slot = HashSlot(&q.qname);
                     CacheGroup *cg = CacheGroupForName(m, slot, q.qnamehash, &q.qname);
-                    if (!mDNSOpaque16IsZero(response->h.id)) unicastQuestion = qptr;
+                    if (!mDNSOpaque16IsZero(response->h.id))
+                    {
+                        unicastQuestion = qptr;
+                        if (qptr->qDNSServer && DNSSECQuestion(qptr))
+                        {
+                            LogInfo("mDNSCoreReceiveResponse: Setting aware for %##s (%s) on %#a", qptr->qname.c,
+                                DNSTypeName(qptr->qtype), &qptr->qDNSServer->addr);
+                            qptr->qDNSServer->DNSSECAware = mDNStrue;
+                            qptr->qDNSServer->req_DO = mDNStrue;
+                        }
+                        if (qptr->ValidatingResponse)
+                            DNSSECQuestion = mDNStrue;
+                    }
                     for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
                         if (SameNameRecordAnswersQuestion(&rr->resrec, qptr))
                         {
                     for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
                         if (SameNameRecordAnswersQuestion(&rr->resrec, qptr))
                         {
@@ -7420,14 +8821,36 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m,
                             // Don't want to disturb rroriginalttl here, because code below might need it for the exponential backoff doubling algorithm
                             rr->TimeRcvd          = m->timenow - TicksTTL(rr) - 1;
                             rr->UnansweredQueries = MaxUnansweredQueries;
                             // Don't want to disturb rroriginalttl here, because code below might need it for the exponential backoff doubling algorithm
                             rr->TimeRcvd          = m->timenow - TicksTTL(rr) - 1;
                             rr->UnansweredQueries = MaxUnansweredQueries;
+                            rr->CRDNSSECQuestion = 0;
+                            if (unicastQuestion && DNSSECQuestion(unicastQuestion))
+                            {
+                                LogInfo("mDNSCoreReceiveResponse: CRDNSSECQuestion set for record %s, question %##s (%s)", CRDisplayString(m, rr),
+                                    unicastQuestion->qname.c, DNSTypeName(unicastQuestion->qtype));
+                                rr->CRDNSSECQuestion = 1;
+                            }
                         }
                 }
                 else
                 {
                     if (qptr)
                     {
                         }
                 }
                 else
                 {
                     if (qptr)
                     {
-                        LogInfo("mDNSCoreReceiveResponse: Server %p responded with code %d to query %##s (%s)", qptr->qDNSServer, rcode, q.qname.c, DNSTypeName(q.qtype));
-                        PenalizeDNSServer(m, qptr);
+                        // If we recv any error from the DNSServer for a DNSSEC Query and if we know that the server
+                        // is not DNSSEC aware, stop doing DNSSEC for that DNSServer. Note that by setting the
+                        // req_DO to false here, the next retransmission for this question will turn off validation
+                        // and hence retransmit without the EDNS0/DOK option.
+                        if (DNSSECOptionalQuestion(qptr) && qptr->qDNSServer && !qptr->qDNSServer->DNSSECAware)
+                        {
+                            LogInfo("mDNSCoreReceiveResponse: Server %p responded with code %d to DNSSEC Query %##s (%s), clear DO flag", 
+                                    qptr->qDNSServer, rcode, q.qname.c, DNSTypeName(q.qtype));
+                            qptr->qDNSServer->req_DO = mDNSfalse;    
+                        }
+                        // For Unicast DNS Queries, penalize the DNSServer
+                        else
+                        {
+                            LogInfo("mDNSCoreReceiveResponse: Server %p responded with code %d to query %##s (%s)", 
+                                    qptr->qDNSServer, rcode, q.qname.c, DNSTypeName(q.qtype));
+                            PenalizeDNSServer(m, qptr, response->h.flags);
+                        }
                     }
                     returnEarly = mDNStrue;
                 }
                     }
                     returnEarly = mDNStrue;
                 }
@@ -7443,8 +8866,18 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m,
             // generate negative cache entries (we want to query the next server)
             return;
         }
             // generate negative cache entries (we want to query the next server)
             return;
         }
+        if (unicastQuestion && DNSSECQuestion(unicastQuestion))
+        {
+            BumpDNSSECStats(m, kStatsActionSet, kStatsTypeMsgSize, (end - response->data));
+        }
     }
 
     }
 
+    // Parse the NSEC3 records from the Authority section before we process
+    // the Answer section so that we can cache them along with the proper
+    // cache records we create.
+    if (mDNSOpaque16IsZero(response->h.id))
+        mDNSParseNSEC3Records(m, response, end, InterfaceID, &McastNSEC3Records);
+
     for (i = 0; i < totalrecords && ptr && ptr < end; i++)
     {
         // All responses sent via LL multicast are acceptable for caching
     for (i = 0; i < totalrecords && ptr && ptr < end; i++)
     {
         // All responses sent via LL multicast are acceptable for caching
@@ -7458,10 +8891,26 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m,
             (i < firstadditional) ? (mDNSu8)kDNSRecordTypePacketAuth : (mDNSu8)kDNSRecordTypePacketAdd;
         ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, RecordType, &m->rec);
         if (!ptr) goto exit;        // Break out of the loop and clean up our CacheFlushRecords list before exiting
             (i < firstadditional) ? (mDNSu8)kDNSRecordTypePacketAuth : (mDNSu8)kDNSRecordTypePacketAdd;
         ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, RecordType, &m->rec);
         if (!ptr) goto exit;        // Break out of the loop and clean up our CacheFlushRecords list before exiting
-        if (m->rec.r.resrec.RecordType == kDNSRecordTypePacketNegative) { m->rec.r.resrec.RecordType = 0; continue; }
 
 
+        if (m->rec.r.resrec.RecordType == kDNSRecordTypePacketNegative)
+        {
+            mDNSCoreResetRecord(m);
+            continue;
+        }
+
+        // We have already parsed the NSEC3 records and cached them approrpriately for
+        // multicast responses.
+        if (mDNSOpaque16IsZero(response->h.id) && m->rec.r.resrec.rrtype == kDNSType_NSEC3)
+        {
+            mDNSCoreResetRecord(m);
+            continue;
+        }
         // Don't want to cache OPT or TSIG pseudo-RRs
         // Don't want to cache OPT or TSIG pseudo-RRs
-        if (m->rec.r.resrec.rrtype == kDNSType_TSIG) { m->rec.r.resrec.RecordType = 0; continue; }
+        if (m->rec.r.resrec.rrtype == kDNSType_TSIG)
+        {
+            mDNSCoreResetRecord(m);
+            continue;
+        }
         if (m->rec.r.resrec.rrtype == kDNSType_OPT)
         {
             const rdataOPT *opt;
         if (m->rec.r.resrec.rrtype == kDNSType_OPT)
         {
             const rdataOPT *opt;
@@ -7474,15 +8923,14 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m,
                     ClearProxyRecords(m, &opt->u.owner, m->DuplicateRecords);
                     ClearProxyRecords(m, &opt->u.owner, m->ResourceRecords);
                 }
                     ClearProxyRecords(m, &opt->u.owner, m->DuplicateRecords);
                     ClearProxyRecords(m, &opt->u.owner, m->ResourceRecords);
                 }
-            m->rec.r.resrec.RecordType = 0;
+            mDNSCoreResetRecord(m);
             continue;
         }
             continue;
         }
-
         // if a CNAME record points to itself, then don't add it to the cache
         if ((m->rec.r.resrec.rrtype == kDNSType_CNAME) && SameDomainName(m->rec.r.resrec.name, &m->rec.r.resrec.rdata->u.name))
         {
             LogInfo("mDNSCoreReceiveResponse: CNAME loop domain name %##s", m->rec.r.resrec.name->c);
         // if a CNAME record points to itself, then don't add it to the cache
         if ((m->rec.r.resrec.rrtype == kDNSType_CNAME) && SameDomainName(m->rec.r.resrec.name, &m->rec.r.resrec.rdata->u.name))
         {
             LogInfo("mDNSCoreReceiveResponse: CNAME loop domain name %##s", m->rec.r.resrec.name->c);
-            m->rec.r.resrec.RecordType = 0;
+            mDNSCoreResetRecord(m);
             continue;
         }
 
             continue;
         }
 
@@ -7505,6 +8953,14 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m,
                 // has already matched the question using the 64 bit Id in the packet and we use that here.
 
                 if (llqMatch != mDNSNULL) m->rec.r.resrec.rDNSServer = uDNSServer = llqMatch->qDNSServer;
                 // has already matched the question using the 64 bit Id in the packet and we use that here.
 
                 if (llqMatch != mDNSNULL) m->rec.r.resrec.rDNSServer = uDNSServer = llqMatch->qDNSServer;
+
+                // If this is a DNSSEC question that is also LongLived, don't accept records from the
+                // Additional/Authority section blindly. We need to go through IsAcceptableResponse below
+                // so that NSEC/NSEC3 record are cached in the nseclist if we accept them. This can happen
+                // for both negative responses and wildcard expanded positive responses as both of come
+                // back with NSEC/NSEC3s.
+                if (unicastQuestion && DNSSECQuestion(unicastQuestion))
+                    AcceptableResponse = mDNSfalse;
             }
             else if (!AcceptableResponse || !dstaddr)
             {
             }
             else if (!AcceptableResponse || !dstaddr)
             {
@@ -7513,36 +8969,69 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m,
                 // Even though it is AcceptableResponse, we still need a DNSServer pointer for the resource records that
                 // we create.
 
                 // Even though it is AcceptableResponse, we still need a DNSServer pointer for the resource records that
                 // we create.
 
-                DNSQuestion *q = ExpectingUnicastResponseForRecord(m, srcaddr, ResponseSrcLocal, dstport, response->h.id, &m->rec.r, !dstaddr);
+                if (!mDNSOpaque16IsZero(response->h.id))
+                {
+                    DNSQuestion *q = ExpectingUnicastResponseForRecord(m, srcaddr, ResponseSrcLocal, dstport, response->h.id, &m->rec.r, !dstaddr);
 
 
-                // Intialize the DNS server on the resource record which will now filter what questions we answer with
-                // this record.
-                //
-                // We could potentially lookup the DNS server based on the source address, but that may not work always
-                // and that's why ExpectingUnicastResponseForRecord does not try to verify whether the response came
-                // from the DNS server that queried. We follow the same logic here. If we can find a matching quetion based
-                // on the "id" and "source port", then this response answers the question and assume the response
-                // came from the same DNS server that we sent the query to.
+                    // Initialize the DNS server on the resource record which will now filter what questions we answer with
+                    // this record.
+                    //
+                    // We could potentially lookup the DNS server based on the source address, but that may not work always
+                    // and that's why ExpectingUnicastResponseForRecord does not try to verify whether the response came
+                    // from the DNS server that queried. We follow the same logic here. If we can find a matching quetion based
+                    // on the "id" and "source port", then this response answers the question and assume the response
+                    // came from the same DNS server that we sent the query to.
 
 
-                if (q != mDNSNULL)
-                {
-                    AcceptableResponse = mDNStrue;
-                    if (!InterfaceID)
+                    if (q != mDNSNULL)
                     {
                     {
-                        debugf("mDNSCoreReceiveResponse: InterfaceID %p %##s (%s)", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype));
-                        m->rec.r.resrec.rDNSServer = uDNSServer = q->qDNSServer;
+                        AcceptableResponse = mDNStrue;
+                        if (!InterfaceID)
+                        {
+                            debugf("mDNSCoreReceiveResponse: InterfaceID %p %##s (%s)", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype));
+                            m->rec.r.resrec.rDNSServer = uDNSServer = q->qDNSServer;
+                        }
+                        else
+                            LogInfo("mDNSCoreReceiveResponse: InterfaceID %p %##s (%s)", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype));
                     }
                     }
-                }
-                else
-                {
-                    // If we can't find a matching question, we need to see whether we have seen records earlier that matched
-                    // the question. The code below does that. So, make this record unacceptable for now
-                    if (!InterfaceID)
+                    else
                     {
                     {
-                        debugf("mDNSCoreReceiveResponse: Can't find question for record name %##s", m->rec.r.resrec.name->c);
-                        AcceptableResponse = mDNSfalse;
+                        // If we can't find a matching question, we need to see whether we have seen records earlier that matched
+                        // the question. The code below does that. So, make this record unacceptable for now
+                        if (!InterfaceID)
+                        {
+                            debugf("mDNSCoreReceiveResponse: Can't find question for record name %##s", m->rec.r.resrec.name->c);
+                            AcceptableResponse = mDNSfalse;
+                        }
                     }
                 }
                     }
                 }
+                else if (ExpectingMulticastResponseForRecord(m, &m->rec.r, srcaddr, recordAccepted, &McastNSEC3Records))
+                {
+                    recordAccepted = mDNStrue;
+                    AcceptableResponse = mDNStrue;
+                    LogInfo("mDNSCoreReceiveResponse: Accepting record in response to QU question %s, InterfaceID %p", CRDisplayString(m, &m->rec.r),
+                        InterfaceID);
+                }
+                else if (IsDeviceInfoRecord(m->rec.r.resrec.name))
+                {
+                    recordAccepted = mDNStrue;
+                    AcceptableResponse = mDNStrue;
+                    LogInfo("mDNSCoreReceiveResponse: Accepting _device-info record %s, InterfaceID %p",
+                         CRDisplayString(m, &m->rec.r), InterfaceID);
+                }
+            }
+        }
+        else if (llintf && llintf->IgnoreIPv4LL && m->rec.r.resrec.rrtype == kDNSType_A)
+        {
+            CacheRecord *const rr = &m->rec.r;
+            RDataBody2 *const rdb = (RDataBody2 *)rr->smallrdatastorage.data;
+
+            // If we are supposed to ignore link-local addresses on this interface, drop
+            // all "A" records that have link-local address in them.
+            if (mDNSv4AddressIsLinkLocal(&rdb->ipv4))
+            {
+                LogInfo("mDNSResponder: Dropping LinkLocal packet %s", CRDisplayString(m, &m->rec.r));
+                mDNSCoreResetRecord(m);
+                continue;
             }
         }
 
             }
         }
 
@@ -7621,6 +9110,8 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m,
                                 if (!mDNSCoreRegisteredProxyRecord(m, rr))
                                 {
                                     LogMsg("mDNSCoreReceiveResponse: ProbeCount %d; will deregister %s", rr->ProbeCount, ARDisplayString(m, rr));
                                 if (!mDNSCoreRegisteredProxyRecord(m, rr))
                                 {
                                     LogMsg("mDNSCoreReceiveResponse: ProbeCount %d; will deregister %s", rr->ProbeCount, ARDisplayString(m, rr));
+                                    
+                                    m->mDNSStats.NameConflicts++;
                                     mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict);
                                 }
                             }
                                     mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict);
                                 }
                             }
@@ -7632,6 +9123,7 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m,
                             else if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique)
                             {
                                 LogMsg("mDNSCoreReceiveResponse: Unexpected conflict discarding %s", ARDisplayString(m, rr));
                             else if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique)
                             {
                                 LogMsg("mDNSCoreReceiveResponse: Unexpected conflict discarding %s", ARDisplayString(m, rr));
+                                m->mDNSStats.KnownUniqueNameConflicts++;
                                 mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict);
                             }
                             else
                                 mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict);
                             }
                             else
@@ -7664,127 +9156,54 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m,
         {
             const mDNSu32 slot = HashSlot(m->rec.r.resrec.name);
             CacheGroup *cg = CacheGroupForRecord(m, slot, &m->rec.r.resrec);
         {
             const mDNSu32 slot = HashSlot(m->rec.r.resrec.name);
             CacheGroup *cg = CacheGroupForRecord(m, slot, &m->rec.r.resrec);
-            CacheRecord *rr;
+            CacheRecord *rr = mDNSNULL;
 
 
-            // 2a. Check if this packet resource record is already in our cache
-            for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
+            if (McastNSEC3Records)
+                InitializeAnonInfoForCR(m, &McastNSEC3Records, &m->rec.r);
+
+            // 2a. Check if this packet resource record is already in our cache.
+            //
+            // If this record should go in the nseclist, don't look in the cache for updating it.
+            // They are supposed to be cached under the "nsec" field of the cache record for
+            // validation. Just create the cache record. 
+            if (!nseclist)
+            {
+                rr = mDNSCoreReceiveCacheCheck(m, response, LLQType, slot, cg, unicastQuestion, &cfp, &NSECCachePtr, InterfaceID);
+            }
+
+            // If mDNSOppCaching is set (which affects only multicast), enable opportunistic caching in which case we cache
+            // everything that was received over multicast. Otherwise, we are selective about the caching.
+            //
+            // Cache everything that is from ourselves (that's how we answer any questions looking for them). Otherwise call
+            // ExpectingMulticastResponseForRecord which decides whether to cache this record or not.
+            //
+            if (!m->mDNSOppCaching && !rr && !myself && mDNSOpaque16IsZero(response->h.id))
             {
             {
-                mDNSBool match;
-                // Resource record received via unicast, the resGroupID should match ?
-                if (!InterfaceID)
+                if (!ExpectingMulticastResponseForRecord(m, &m->rec.r, srcaddr, recordAccepted, &McastNSEC3Records))
                 {
                 {
-                    mDNSu16 id1 = (rr->resrec.rDNSServer ? rr->resrec.rDNSServer->resGroupID : 0);
-                    mDNSu16 id2 = (m->rec.r.resrec.rDNSServer ? m->rec.r.resrec.rDNSServer->resGroupID : 0);
-                    match = (id1 == id2);
+                    //LogMsg("mDNSCoreReceiveResponse: discarding %s", CRDisplayString(m, &m->rec.r));
+                    mDNSCoreResetRecord(m);
+                    continue;
                 }
                 else
                 }
                 else
-                    match = (rr->resrec.InterfaceID == InterfaceID);
-                // If we found this exact resource record, refresh its TTL
-                if (match && IdenticalSameNameRecord(&m->rec.r.resrec, &rr->resrec))
                 {
                 {
-                    if (m->rec.r.resrec.rdlength > InlineCacheRDSize)
-                        verbosedebugf("Found record size %5d interface %p already in cache: %s",
-                                      m->rec.r.resrec.rdlength, InterfaceID, CRDisplayString(m, &m->rec.r));
-
-                    if (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask)
-                    {
-                        // If this packet record has the kDNSClass_UniqueRRSet flag set, then add it to our cache flushing list
-                        if (rr->NextInCFList == mDNSNULL && cfp != &rr->NextInCFList && LLQType != uDNS_LLQ_Events)
-                        { *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; }
-
-                        // If this packet record is marked unique, and our previous cached copy was not, then fix it
-                        if (!(rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask))
-                        {
-                            DNSQuestion *q;
-                            for (q = m->Questions; q; q=q->next) if (ResourceRecordAnswersQuestion(&rr->resrec, q)) q->UniqueAnswers++;
-                            rr->resrec.RecordType = m->rec.r.resrec.RecordType;
-                        }
-                    }
-
-                    if (!SameRDataBody(&m->rec.r.resrec, &rr->resrec.rdata->u, SameDomainNameCS))
-                    {
-                        // If the rdata of the packet record differs in name capitalization from the record in our cache
-                        // then mDNSPlatformMemSame will detect this. In this case, throw the old record away, so that clients get
-                        // a 'remove' event for the record with the old capitalization, and then an 'add' event for the new one.
-                        // <rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing
-                        rr->resrec.rroriginalttl = 0;
-                        rr->TimeRcvd = m->timenow;
-                        rr->UnansweredQueries = MaxUnansweredQueries;
-                        SetNextCacheCheckTimeForRecord(m, rr);
-                        LogInfo("Discarding due to domainname case change old: %s", CRDisplayString(m,rr));
-                        LogInfo("Discarding due to domainname case change new: %s", CRDisplayString(m,&m->rec.r));
-                        LogInfo("Discarding due to domainname case change in %d slot %3d in %d %d",
-                                NextCacheCheckEvent(rr) - m->timenow, slot, m->rrcache_nextcheck[slot] - m->timenow, m->NextCacheCheck - m->timenow);
-                        // DO NOT break out here -- we want to continue as if we never found it
-                    }
-                    else if (m->rec.r.resrec.rroriginalttl > 0)
-                    {
-                        DNSQuestion *q;
-                        //if (rr->resrec.rroriginalttl == 0) LogMsg("uDNS rescuing %s", CRDisplayString(m, rr));
-                        RefreshCacheRecord(m, rr, m->rec.r.resrec.rroriginalttl);
-
-                        // If we may have NSEC records returned with the answer (which we don't know yet as it
-                        // has not been processed), we need to cache them along with the first cache
-                        // record in the list that answers the question so that it can be used for validation
-                        // later.
-                        if (response->h.numAnswers && unicastQuestion && !NSECCachePtr)
-                        {
-                            LogInfo("mDNSCoreReceiveResponse: rescuing RR %s", CRDisplayString(m, rr));
-                            NSECCachePtr = rr;
-                        }
-                        // We have to reset the question interval to MaxQuestionInterval so that we don't keep
-                        // polling the network once we get a valid response back. For the first time when a new
-                        // cache entry is created, AnswerCurrentQuestionWithResourceRecord does that.
-                        // Subsequently, if we reissue questions from within the mDNSResponder e.g., DNS server
-                        // configuration changed, without flushing the cache, we reset the question interval here.
-                        // Currently, we do this for for both multicast and unicast questions as long as the record
-                        // type is unique. For unicast, resource record is always unique and for multicast it is
-                        // true for records like A etc. but not for PTR.
-                        if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)
-                        {
-                            for (q = m->Questions; q; q=q->next)
-                            {
-                                if (!q->DuplicateOf && !q->LongLived &&
-                                    ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q))
-                                {
-                                    ResetQuestionState(m, q);
-                                    debugf("mDNSCoreReceiveResponse: Set MaxQuestionInterval for %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype));
-                                    break;      // Why break here? Aren't there other questions we might want to look at?-- SC July 2010
-                                }
-                            }
-                        }
-                        break;
-                    }
-                    else
-                    {
-                        // If the packet TTL is zero, that means we're deleting this record.
-                        // To give other hosts on the network a chance to protest, we push the deletion
-                        // out one second into the future. Also, we set UnansweredQueries to MaxUnansweredQueries.
-                        // Otherwise, we'll do final queries for this record at 80% and 90% of its apparent
-                        // lifetime (800ms and 900ms from now) which is a pointless waste of network bandwidth.
-                        // If record's current expiry time is more than a second from now, we set it to expire in one second.
-                        // If the record is already going to expire in less than one second anyway, we leave it alone --
-                        // we don't want to let the goodbye packet *extend* the record's lifetime in our cache.
-                        debugf("DE for %s", CRDisplayString(m, rr));
-                        if (RRExpireTime(rr) - m->timenow > mDNSPlatformOneSecond)
-                        {
-                            rr->resrec.rroriginalttl = 1;
-                            rr->TimeRcvd = m->timenow;
-                            rr->UnansweredQueries = MaxUnansweredQueries;
-                            SetNextCacheCheckTimeForRecord(m, rr);
-                        }
-                        break;
-                    }
+                    recordAccepted = mDNStrue;
                 }
             }
 
                 }
             }
 
+
             // If packet resource record not in our cache, add it now
             // (unless it is just a deletion of a record we never had, in which case we don't care)
             if (!rr && m->rec.r.resrec.rroriginalttl > 0)
             {
                 const mDNSBool AddToCFList = (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && (LLQType != uDNS_LLQ_Events);
             // If packet resource record not in our cache, add it now
             // (unless it is just a deletion of a record we never had, in which case we don't care)
             if (!rr && m->rec.r.resrec.rroriginalttl > 0)
             {
                 const mDNSBool AddToCFList = (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && (LLQType != uDNS_LLQ_Events);
-                const mDNSs32 delay = AddToCFList ? NonZeroTime(m->timenow + mDNSPlatformOneSecond) :
-                                      CheckForSoonToExpireRecords(m, m->rec.r.resrec.name, m->rec.r.resrec.namehash, slot);
+                mDNSs32 delay;
+
+                if (AddToCFList)
+                    delay = NonZeroTime(m->timenow + mDNSPlatformOneSecond);
+                else
+                    delay = CheckForSoonToExpireRecords(m, m->rec.r.resrec.name, m->rec.r.resrec.namehash, slot, mDNSNULL);
+
                 // If unique, assume we may have to delay delivery of this 'add' event.
                 // Below, where we walk the CacheFlushRecords list, we either call CacheRecordDeferredAdd()
                 // to immediately to generate answer callbacks, or we call ScheduleNextCacheCheckTime()
                 // If unique, assume we may have to delay delivery of this 'add' event.
                 // Below, where we walk the CacheFlushRecords list, we either call CacheRecordDeferredAdd()
                 // to immediately to generate answer callbacks, or we call ScheduleNextCacheCheckTime()
@@ -7792,20 +9211,57 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m,
                 rr = CreateNewCacheEntry(m, slot, cg, delay, !nseclist, srcaddr);
                 if (rr)
                 {
                 rr = CreateNewCacheEntry(m, slot, cg, delay, !nseclist, srcaddr);
                 if (rr)
                 {
-                    // NSEC Records and its signatures are cached with the negative cache entry
+                    rr->responseFlags = response->h.flags;
+                    // If we are not creating signatures, then we need to inform DNSSEC so that
+                    // it does not wait forever. Don't do this if we got NSEC records
+                    // as it indicates that this name does not exist.
+                    if (rr->resrec.rrtype == kDNSType_RRSIG && !nseclist)
+                    {
+                        rrsigsCreated = mDNStrue;
+                    }
+                    // Remember whether we created a cache record in response to a DNSSEC question.
+                    // This helps DNSSEC code not to reissue the question to fetch the DNSSEC records.
+                    rr->CRDNSSECQuestion = 0;
+                    if (unicastQuestion && DNSSECQuestion(unicastQuestion))
+                    {
+                        LogInfo("mDNSCoreReceiveResponse: CRDNSSECQuestion set for new record %s, question %##s (%s)", CRDisplayString(m, rr),
+                            unicastQuestion->qname.c, DNSTypeName(unicastQuestion->qtype));
+                        rr->CRDNSSECQuestion = 1;
+                    }
+                    // NSEC/NSEC3 records and its signatures are cached with the negative cache entry
                     // which we should be creating below. It is also needed in the wildcard
                     // expanded answer case and in that case it is cached along with the answer.
                     // which we should be creating below. It is also needed in the wildcard
                     // expanded answer case and in that case it is cached along with the answer.
-                    if (nseclist) { *nsecp = rr; nsecp = &rr->next; }
-                    else if (AddToCFList) { *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; }
-                    else if (rr->DelayDelivery) ScheduleNextCacheCheckTime(m, slot, rr->DelayDelivery);
+                    if (nseclist)
+                    {
+                        rr->TimeRcvd = m->timenow;
+                        *nsecp = rr;
+                        nsecp = &rr->next;
+                    }
+                    else if (AddToCFList)
+                    {
+                        *cfp = rr;
+                        cfp = &rr->NextInCFList;
+                        *cfp = (CacheRecord*)1;
+                    }
+                    else if (rr->DelayDelivery)
+                    {
+                        ScheduleNextCacheCheckTime(m, slot, rr->DelayDelivery);
+                    }
+                }
+            }
+            else
+            {
+                if (rr && rr->resrec.AnonInfo && m->rec.r.resrec.AnonInfo)
+                {
+                    CopyAnonInfoForCR(m, rr, &m->rec.r);
                 }
             }
         }
                 }
             }
         }
-        m->rec.r.resrec.RecordType = 0;     // Clear RecordType to show we're not still using it
+        mDNSCoreResetRecord(m);
     }
 
 exit:
     }
 
 exit:
-    m->rec.r.resrec.RecordType = 0;     // Clear RecordType to show we're not still using it
+    mDNSCoreResetRecord(m);
 
     // If we've just received one or more records with their cache flush bits set,
     // then scan that cache slot to see if there are any old stale records we need to flush
 
     // If we've just received one or more records with their cache flush bits set,
     // then scan that cache slot to see if there are any old stale records we need to flush
@@ -7950,13 +9406,13 @@ exit:
                 // might start the verification process which needs these NSEC records
                 if (!AddNSECSForCacheRecord(m, NSECRecords, NSECCachePtr, rcode))
                 { 
                 // might start the verification process which needs these NSEC records
                 if (!AddNSECSForCacheRecord(m, NSECRecords, NSECCachePtr, rcode))
                 { 
-                    LogMsg("mDNSCoreReceiveResponse: AddNSECSForCacheRecord failed to add NSEC for %s", CRDisplayString(m, NSECCachePtr));
+                    LogInfo("mDNSCoreReceiveResponse: AddNSECSForCacheRecord failed to add NSEC for %s", CRDisplayString(m, NSECCachePtr));
                     FreeNSECRecords(m, NSECRecords);
                 }
                 NSECRecords = mDNSNULL;
                 NSECCachePtr = mDNSNULL;
             }
                     FreeNSECRecords(m, NSECRecords);
                 }
                 NSECRecords = mDNSNULL;
                 NSECCachePtr = mDNSNULL;
             }
-            r1->DelayDelivery = CheckForSoonToExpireRecords(m, r1->resrec.name, r1->resrec.namehash, slot);
+            r1->DelayDelivery = CheckForSoonToExpireRecords(m, r1->resrec.name, r1->resrec.namehash, slot, mDNSNULL);
             // If no longer delaying, deliver answer now, else schedule delivery for the appropriate time
             if (!r1->DelayDelivery) CacheRecordDeferredAdd(m, r1);
             else ScheduleNextCacheCheckTime(m, slot, r1->DelayDelivery);
             // If no longer delaying, deliver answer now, else schedule delivery for the appropriate time
             if (!r1->DelayDelivery) CacheRecordDeferredAdd(m, r1);
             else ScheduleNextCacheCheckTime(m, slot, r1->DelayDelivery);
@@ -7970,15 +9426,34 @@ exit:
         LogInfo("mDNSCoreReceieveResponse: Updating NSEC records in %s", CRDisplayString(m, NSECCachePtr));
         if (!AddNSECSForCacheRecord(m, NSECRecords, NSECCachePtr, rcode))
         { 
         LogInfo("mDNSCoreReceieveResponse: Updating NSEC records in %s", CRDisplayString(m, NSECCachePtr));
         if (!AddNSECSForCacheRecord(m, NSECRecords, NSECCachePtr, rcode))
         { 
-            LogMsg("mDNSCoreReceiveResponse: AddNSECSForCacheRecord failed to add NSEC for %s", CRDisplayString(m, NSECCachePtr));
+            LogInfo("mDNSCoreReceiveResponse: AddNSECSForCacheRecord failed to add NSEC for %s", CRDisplayString(m, NSECCachePtr));
             FreeNSECRecords(m, NSECRecords);
         }
         NSECRecords = mDNSNULL;
         NSECCachePtr = mDNSNULL;
     }
 
             FreeNSECRecords(m, NSECRecords);
         }
         NSECRecords = mDNSNULL;
         NSECCachePtr = mDNSNULL;
     }
 
+    // If there is at least one answer and we did not create RRSIGs and there was a
+    // ValidatingResponse question waiting for this response, give a hint that no RRSIGs
+    // were created. We don't need to give a hint:
+    //
+    // - if we have no answers, the mDNSCoreReceiveNoUnicastAnswers below should
+    //   generate a negative response
+    //
+    // - if we have NSECRecords, it means we might have a potential proof for
+    //   non-existence of name that we are looking for
+    //
+    if (response->h.numAnswers && !rrsigsCreated && DNSSECQuestion && !NSECRecords)
+        mDNSCoreReceiveNoDNSSECAnswers(m, response, end, dstaddr, dstport, InterfaceID);
+
     // See if we need to generate negative cache entries for unanswered unicast questions
     mDNSCoreReceiveNoUnicastAnswers(m, response, end, dstaddr, dstport, InterfaceID, LLQType, rcode, NSECRecords);
     // See if we need to generate negative cache entries for unanswered unicast questions
     mDNSCoreReceiveNoUnicastAnswers(m, response, end, dstaddr, dstport, InterfaceID, LLQType, rcode, NSECRecords);
+
+    if (McastNSEC3Records)
+    {
+        debugf("mDNSCoreReceiveResponse: McastNSEC3Records not used");
+        FreeNSECRecords(m, McastNSEC3Records);
+    }
 }
 
 // ScheduleWakeup causes all proxy records with WakeUp.HMAC matching mDNSEthAddr 'e' to be deregistered, causing
 }
 
 // ScheduleWakeup causes all proxy records with WakeUp.HMAC matching mDNSEthAddr 'e' to be deregistered, causing
@@ -8044,6 +9519,37 @@ mDNSlocal void SPSRecordCallback(mDNS *const m, AuthRecord *const ar, mStatus re
     }
 }
 
     }
 }
 
+mDNSlocal mDNSu8 *GetValueForMACAddr(mDNSu8 *ptr, mDNSu8 *limit, mDNSEthAddr *eth)
+{
+    int     i;
+    mDNSs8  hval   = 0;
+    int     colons = 0;
+    mDNSu8  val    = 0;
+
+    for (i = 0; ptr < limit && *ptr != ' ' && i < 17; i++, ptr++)
+    {
+        hval = HexVal(*ptr);
+        if (hval != -1)
+        {
+            val <<= 4;
+            val |= hval;
+        }
+        else if (*ptr == ':')
+        {
+            eth->b[colons] = val;
+            colons++;
+            val = 0;
+        }
+    }
+    if (colons != 5)
+    {
+        LogMsg("GetValueForMACAddr: Address malformed colons %d", colons);
+        return mDNSNULL;
+    }
+    eth->b[colons] = val;
+    return ptr;
+}
+
 mDNSlocal mDNSu8 *GetValueForIPv6Addr(mDNSu8 *ptr, mDNSu8 *limit, mDNSv6Addr *v6)
 {
     int hval;
 mDNSlocal mDNSu8 *GetValueForIPv6Addr(mDNSu8 *ptr, mDNSu8 *limit, mDNSv6Addr *v6)
 {
     int hval;
@@ -8151,12 +9657,11 @@ mDNSlocal mDNSu8 *GetValueForIPv6Addr(mDNSu8 *ptr, mDNSu8 *limit, mDNSv6Addr *v6
 
 mDNSlocal mDNSu8 *GetValueForIPv4Addr(mDNSu8 *ptr, mDNSu8 *limit, mDNSv4Addr *v4)
 {
 
 mDNSlocal mDNSu8 *GetValueForIPv4Addr(mDNSu8 *ptr, mDNSu8 *limit, mDNSv4Addr *v4)
 {
-    int i;
     mDNSu32 val;
     int dots = 0;
     mDNSu32 val;
     int dots = 0;
-
     val = 0;
     val = 0;
-    for (i = 0; ptr < limit && *ptr != ' '; ptr++)
+
+    for ( ; ptr < limit && *ptr != ' '; ptr++)
     {
         if (*ptr >= '0' &&  *ptr <= '9')
             val = val * 10 + *ptr - '0';
     {
         if (*ptr >= '0' &&  *ptr <= '9')
             val = val * 10 + *ptr - '0';
@@ -8183,11 +9688,10 @@ mDNSlocal mDNSu8 *GetValueForIPv4Addr(mDNSu8 *ptr, mDNSu8 *limit, mDNSv4Addr *v4
 
 mDNSlocal mDNSu8 *GetValueForKeepalive(mDNSu8 *ptr, mDNSu8 *limit, mDNSu32 *value)
 {
 
 mDNSlocal mDNSu8 *GetValueForKeepalive(mDNSu8 *ptr, mDNSu8 *limit, mDNSu32 *value)
 {
-    int i;
     mDNSu32 val;
 
     val = 0;
     mDNSu32 val;
 
     val = 0;
-    for (i = 0; ptr < limit && *ptr != ' '; ptr++)
+    for ( ; ptr < limit && *ptr != ' '; ptr++)
     {
         if (*ptr < '0' || *ptr > '9')
         {
     {
         if (*ptr < '0' || *ptr > '9')
         {
@@ -8205,7 +9709,7 @@ mDNSlocal mDNSu8 *GetValueForKeepalive(mDNSu8 *ptr, mDNSu8 *limit, mDNSu32 *valu
     return ptr;
 }
 
     return ptr;
 }
 
-mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSAddr *laddr, mDNSAddr *raddr, mDNSu32 *seq,
+mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSAddr *laddr, mDNSAddr *raddr, mDNSEthAddr *eth, mDNSu32 *seq,
                                          mDNSu32 *ack, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu16 *win)
 {
     if (ar->resrec.rrtype != kDNSType_NULL)
                                          mDNSu32 *ack, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu16 *win)
 {
     if (ar->resrec.rrtype != kDNSType_NULL)
@@ -8216,13 +9720,11 @@ mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSA
         int len = ar->resrec.rdlength;
         mDNSu8 *ptr = &ar->resrec.rdata->u.txt.c[1];
         mDNSu8 *limit = ptr + len - 1; // Exclude the first byte that is the length
         int len = ar->resrec.rdlength;
         mDNSu8 *ptr = &ar->resrec.rdata->u.txt.c[1];
         mDNSu8 *limit = ptr + len - 1; // Exclude the first byte that is the length
-        mDNSu32 value;
+        mDNSu32 value = 0;
 
         while (ptr < limit)
         {
             mDNSu8 param = *ptr;
 
         while (ptr < limit)
         {
             mDNSu8 param = *ptr;
-            mDNSu8 *p;
-
             ptr += 2;   // Skip the letter and the "="
             if (param == 'h')
             {
             ptr += 2;   // Skip the letter and the "="
             if (param == 'h')
             {
@@ -8244,13 +9746,16 @@ mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSA
                 raddr->type = mDNSAddrType_IPv6;
                 ptr = GetValueForIPv6Addr(ptr, limit, &raddr->ip.v6);
             }
                 raddr->type = mDNSAddrType_IPv6;
                 ptr = GetValueForIPv6Addr(ptr, limit, &raddr->ip.v6);
             }
+            else if (param == 'm')
+            {
+                ptr = GetValueForMACAddr(ptr, limit, eth);
+            }
             else
             {
                 ptr = GetValueForKeepalive(ptr, limit, &value);
             }
             if (!ptr) { LogMsg("mDNS_ExtractKeepaliveInfo: Cannot parse\n"); return; }
 
             else
             {
                 ptr = GetValueForKeepalive(ptr, limit, &value);
             }
             if (!ptr) { LogMsg("mDNS_ExtractKeepaliveInfo: Cannot parse\n"); return; }
 
-            p = (mDNSu8 *)&value;
             // Extract everything in network order so that it is easy for sending a keepalive and also
             // for matching incoming TCP packets
             switch (param)
             // Extract everything in network order so that it is easy for sending a keepalive and also
             // for matching incoming TCP packets
             switch (param)
@@ -8263,26 +9768,27 @@ mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSA
             case 'H':
             case 'd':
             case 'D':
             case 'H':
             case 'd':
             case 'D':
+            case 'm':
+            case 'i':
+            case 'c':
                 break;
             case 'l':
                 break;
             case 'l':
-                lport->NotAnInteger = p[0] << 8 | p[1];
+                lport->NotAnInteger = swap16((mDNSu16)value);
                 break;
             case 'r':
                 break;
             case 'r':
-                rport->NotAnInteger = p[0] << 8 | p[1];
+                rport->NotAnInteger = swap16((mDNSu16)value);
                 break;
             case 's':
                 break;
             case 's':
-                value = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
-                *seq = value;
+                *seq = swap32(value);
                 break;
             case 'a':
                 break;
             case 'a':
-                value = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
-                *ack = value;
+                *ack = swap32(value);
                 break;
             case 'w':
                 break;
             case 'w':
-                *win = p[0] << 8 | p[1];
+                *win = swap16((mDNSu16)value);
                 break;
             default:
                 break;
             default:
-                LogMsg("mDNS_ExtractKeepaliveInfo: unknown value\n");
+                LogMsg("mDNS_ExtractKeepaliveInfo: unknown value %c\n", param);
                 ptr = limit;
                 break;
             }
                 ptr = limit;
                 break;
             }
@@ -8293,11 +9799,12 @@ mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSA
 
 // Matches the proxied auth records to the incoming TCP packet and returns the match and its sequence and ack in "rseq" and "rack" so that
 // the clients need not retrieve this information from the auth record again.
 
 // Matches the proxied auth records to the incoming TCP packet and returns the match and its sequence and ack in "rseq" and "rack" so that
 // the clients need not retrieve this information from the auth record again.
-mDNSlocal AuthRecord* mDNS_MatchKeepaliveInfo(mDNS *const m, const mDNSAddr const* pladdr, const mDNSAddr const* praddr, const mDNSIPPort plport,
+mDNSlocal AuthRecord* mDNS_MatchKeepaliveInfo(mDNS *const m, const mDNSAddr* pladdr, const mDNSAddr* praddr, const mDNSIPPort plport,
                                               const mDNSIPPort prport, mDNSu32 *rseq, mDNSu32 *rack)
 {
     AuthRecord *ar;
     mDNSAddr laddr, raddr;
                                               const mDNSIPPort prport, mDNSu32 *rseq, mDNSu32 *rack)
 {
     AuthRecord *ar;
     mDNSAddr laddr, raddr;
+    mDNSEthAddr eth;
     mDNSIPPort lport, rport;
     mDNSu32 timeout, seq, ack;
     mDNSu16 win;
     mDNSIPPort lport, rport;
     mDNSu32 timeout, seq, ack;
     mDNSu16 win;
@@ -8311,7 +9818,7 @@ mDNSlocal AuthRecord* mDNS_MatchKeepaliveInfo(mDNS *const m, const mDNSAddr cons
 
         if (!ar->WakeUp.HMAC.l[0]) continue;
 
 
         if (!ar->WakeUp.HMAC.l[0]) continue;
 
-        mDNS_ExtractKeepaliveInfo(ar, &timeout, &laddr, &raddr, &seq, &ack, &lport, &rport, &win);
+        mDNS_ExtractKeepaliveInfo(ar, &timeout, &laddr, &raddr, &eth, &seq, &ack, &lport, &rport, &win);
 
         // Did we parse correctly ?
         if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(&raddr) || !seq || !ack || mDNSIPPortIsZero(lport) || mDNSIPPortIsZero(rport) || !win)
 
         // Did we parse correctly ?
         if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(&raddr) || !seq || !ack || mDNSIPPortIsZero(lport) || mDNSIPPortIsZero(rport) || !win)
@@ -8344,6 +9851,7 @@ mDNSlocal void mDNS_SendKeepalives(mDNS *const m)
         mDNSu32 timeout, seq, ack;
         mDNSu16 win;
         mDNSAddr laddr, raddr;
         mDNSu32 timeout, seq, ack;
         mDNSu16 win;
         mDNSAddr laddr, raddr;
+        mDNSEthAddr eth;
         mDNSIPPort lport, rport;
 
         timeout = seq = ack = 0;
         mDNSIPPort lport, rport;
 
         timeout = seq = ack = 0;
@@ -8354,7 +9862,7 @@ mDNSlocal void mDNS_SendKeepalives(mDNS *const m)
 
         if (!ar->WakeUp.HMAC.l[0]) continue;
 
 
         if (!ar->WakeUp.HMAC.l[0]) continue;
 
-        mDNS_ExtractKeepaliveInfo(ar, &timeout, &laddr, &raddr, &seq, &ack, &lport, &rport, &win);
+        mDNS_ExtractKeepaliveInfo(ar, &timeout, &laddr, &raddr, &eth, &seq, &ack, &lport, &rport, &win);
 
         if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(&raddr) || !seq || !ack || mDNSIPPortIsZero(lport) || mDNSIPPortIsZero(rport) || !win)
         {
 
         if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(&raddr) || !seq || !ack || mDNSIPPortIsZero(lport) || mDNSIPPortIsZero(rport) || !win)
         {
@@ -8375,9 +9883,39 @@ mDNSlocal void mDNS_SendKeepalives(mDNS *const m)
     }
 }
 
     }
 }
 
-mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m,
-                                     const DNSMessage *const msg, const mDNSu8 *end,
-                                     const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport,
+mDNSlocal void mDNS_SendKeepaliveACK(mDNS *const m, AuthRecord *ar)
+{
+    if (ar != mDNSNULL)
+    {
+        LogInfo("mDNS_SendKeepalivesACK: AuthRecord is NULL");
+        return;
+    }
+    mDNSu32     timeout, seq, ack;
+    mDNSu16     win;
+    mDNSAddr    laddr, raddr;
+    mDNSEthAddr eth;
+    mDNSIPPort  lport, rport;
+
+    timeout = seq = ack = 0;
+    win = 0;
+
+    laddr = raddr = zeroAddr;
+    lport = rport = zeroIPPort;
+
+    mDNS_ExtractKeepaliveInfo(ar, &timeout, &laddr, &raddr, &eth, &seq, &ack, &lport, &rport, &win);
+
+    if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(&raddr) || !seq || !ack || mDNSIPPortIsZero(lport) || mDNSIPPortIsZero(rport) || !win)
+    {
+        LogInfo("mDNS_SendKeepaliveACK: not a valid record %s for keepalive", ARDisplayString(m, ar));
+        return;
+    }
+    LogMsg("mDNS_SendKeepaliveACK: laddr %#a raddr %#a lport %d rport %d", &laddr, &raddr, mDNSVal16(lport), mDNSVal16(rport));
+    mDNSPlatformSendKeepalive(&laddr, &raddr, &lport, &rport, seq, ack, win);
+}
+
+mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m,
+                                     const DNSMessage *const msg, const mDNSu8 *end,
+                                     const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport,
                                      const mDNSInterfaceID InterfaceID)
 {
     int i;
                                      const mDNSInterfaceID InterfaceID)
 {
     int i;
@@ -8452,6 +9990,11 @@ mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m,
             updatelease = 0x40000000UL / mDNSPlatformOneSecond;
 
         ptr = LocateAuthorities(msg, end);
             updatelease = 0x40000000UL / mDNSPlatformOneSecond;
 
         ptr = LocateAuthorities(msg, end);
+
+        // Clear any stale TCP keepalive records that may exist
+        ClearKeepaliveProxyRecords(m, &owner, m->DuplicateRecords, InterfaceID);
+        ClearKeepaliveProxyRecords(m, &owner, m->ResourceRecords, InterfaceID);
+
         for (i = 0; i < msg->h.mDNS_numUpdates && ptr && ptr < end; i++)
         {
             ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &m->rec);
         for (i = 0; i < msg->h.mDNS_numUpdates && ptr && ptr < end; i++)
         {
             ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &m->rec);
@@ -8459,13 +10002,21 @@ mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m,
             {
                 mDNSu16 RDLengthMem = GetRDLengthMem(&m->rec.r.resrec);
                 AuthRecord *ar = mDNSPlatformMemAllocate(sizeof(AuthRecord) - sizeof(RDataBody) + RDLengthMem);
             {
                 mDNSu16 RDLengthMem = GetRDLengthMem(&m->rec.r.resrec);
                 AuthRecord *ar = mDNSPlatformMemAllocate(sizeof(AuthRecord) - sizeof(RDataBody) + RDLengthMem);
-                if (!ar) { m->omsg.h.flags.b[1] |= kDNSFlag1_RC_Refused; break; }
+                if (!ar)
+                {
+                    m->omsg.h.flags.b[1] |= kDNSFlag1_RC_Refused;
+                    break;
+                }
                 else
                 {
                     mDNSu8 RecordType = m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask ? kDNSRecordTypeUnique : kDNSRecordTypeShared;
                     m->rec.r.resrec.rrclass &= ~kDNSClass_UniqueRRSet;
                 else
                 {
                     mDNSu8 RecordType = m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask ? kDNSRecordTypeUnique : kDNSRecordTypeShared;
                     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);
+                    // All stale keepalive records have been flushed prior to this loop.
+                    if (!mDNS_KeepaliveRecord(&m->rec.r.resrec))
+                    {
+                        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, AuthRecordAny, SPSRecordCallback, ar);
                     AssignDomainName(&ar->namestorage, m->rec.r.resrec.name);
                     ar->resrec.rdlength = GetRDLength(&m->rec.r.resrec, mDNSfalse);
                     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);
@@ -8487,13 +10038,7 @@ mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m,
                         m->NextScheduledSPS = ar->TimeExpire;
                     ar->KATimeExpire = 0;
                     mDNS_Register_internal(m, ar);
                         m->NextScheduledSPS = ar->TimeExpire;
                     ar->KATimeExpire = 0;
                     mDNS_Register_internal(m, ar);
-                    // Unsolicited Neighbor Advertisements (RFC 2461 Section 7.2.6) give us fast address cache updating,
-                    // but some older IPv6 clients get confused by them, so for now we don't send them. Without Unsolicited
-                    // Neighbor Advertisements we have to rely on Neighbor Unreachability Detection instead, which is slower.
-                    // Given this, we'll do our best to wake for existing IPv6 connections, but we don't want to encourage
-                    // new ones for sleeping clients, so we'll we send deletions for our SPS clients' AAAA records.
-                    if (m->KnownBugs & mDNS_KnownBug_LimitedIPv6)
-                        if (ar->resrec.rrtype == kDNSType_AAAA) ar->resrec.rroriginalttl = 0;
+
                     m->ProxyRecords++;
                     mDNS_UpdateAllowSleep(m);
                     LogSPS("SPS Registered %4d %X %s", m->ProxyRecords, RecordType, ARDisplayString(m,ar));
                     m->ProxyRecords++;
                     mDNS_UpdateAllowSleep(m);
                     LogSPS("SPS Registered %4d %X %s", m->ProxyRecords, RecordType, ARDisplayString(m,ar));
@@ -8524,7 +10069,7 @@ mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m,
     mDNS_SendKeepalives(m);
 }
 
     mDNS_SendKeepalives(m);
 }
 
-mDNSlocal void mDNSCoreReceiveUpdateR(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, const mDNSInterfaceID InterfaceID)
+mDNSlocal void mDNSCoreReceiveUpdateR(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID)
 {
     if (InterfaceID)
     {
 {
     if (InterfaceID)
     {
@@ -8577,6 +10122,12 @@ mDNSlocal void mDNSCoreReceiveUpdateR(mDNS *const m, const DNSMessage *const msg
             if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now
                 m->CurrentRecord = rr->next;
         }
             if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now
                 m->CurrentRecord = rr->next;
         }
+
+        // Update the dynamic store with the IP Address and MAC address of the sleep proxy
+        char *ifname = InterfaceNameForID(m, InterfaceID);
+        mDNSAddr spsaddr;
+        mDNSPlatformMemCopy(&spsaddr, srcaddr, sizeof (mDNSAddr));
+        mDNSPlatformStoreSPSMACAddr(&spsaddr, ifname);
     }
     // 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.
     }
     // 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.
@@ -8625,6 +10176,11 @@ mDNSexport void MakeNegativeCacheRecord(mDNS *const m, CacheRecord *const cr,
 #endif
     cr->NextInCFList       = mDNSNULL;
     cr->nsec               = mDNSNULL;
 #endif
     cr->NextInCFList       = mDNSNULL;
     cr->nsec               = mDNSNULL;
+    cr->soa                = mDNSNULL;
+    cr->CRDNSSECQuestion   = 0;
+    // Initialize to the basic one and the caller can set it to more
+    // specific based on the response if any
+    cr->responseFlags      = ResponseFlags;
 }
 
 mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *const end,
 }
 
 mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *const end,
@@ -8657,7 +10213,7 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *co
         if (mDNSSameIPPort(srcport, NATPMPPort))
         {
             mDNS_Lock(m);
         if (mDNSSameIPPort(srcport, NATPMPPort))
         {
             mDNS_Lock(m);
-            uDNS_ReceiveNATPMPPacket(m, InterfaceID, pkt, (mDNSu16)(end - (mDNSu8 *)pkt));
+            uDNS_ReceiveNATPacket(m, InterfaceID, pkt, (mDNSu16)(end - (mDNSu8 *)pkt));
             mDNS_Unlock(m);
             return;
         }
             mDNS_Unlock(m);
             return;
         }
@@ -8688,6 +10244,21 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *co
 
     mDNS_Lock(m);
     m->PktNum++;
 
     mDNS_Lock(m);
     m->PktNum++;
+    if (mDNSOpaque16IsZero(msg->h.id))
+    {
+        m->MPktNum++;
+#if APPLE_OSX_mDNSResponder
+        // Track the number of multicast packets received from a source outside our subnet.
+        // Check the destination address to avoid accounting for spurious packets that
+        // comes in with message id zero.
+        if (!mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr, mDNSNULL) &&
+            mDNSAddressIsAllDNSLinkGroup(dstaddr))
+        {
+            m->RemoteSubnet++;
+        }
+#endif // #if APPLE_OSX_mDNSResponder
+    }
+
 #ifndef UNICAST_DISABLED
     if (!dstaddr || (!mDNSAddressIsAllDNSLinkGroup(dstaddr) && (QR_OP == StdR || QR_OP == UpdR)))
         if (!mDNSOpaque16IsZero(msg->h.id)) // uDNS_ReceiveMsg only needs to get real uDNS responses, not "QU" mDNS responses
 #ifndef UNICAST_DISABLED
     if (!dstaddr || (!mDNSAddressIsAllDNSLinkGroup(dstaddr) && (QR_OP == StdR || QR_OP == UpdR)))
         if (!mDNSOpaque16IsZero(msg->h.id)) // uDNS_ReceiveMsg only needs to get real uDNS responses, not "QU" mDNS responses
@@ -8702,7 +10273,7 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *co
     if      (QR_OP == StdQ) mDNSCoreReceiveQuery   (m, msg, end, srcaddr, srcport, dstaddr, dstport, ifid);
     else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, srcaddr, srcport, dstaddr, dstport, ifid);
     else if (QR_OP == UpdQ) mDNSCoreReceiveUpdate  (m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID);
     if      (QR_OP == StdQ) mDNSCoreReceiveQuery   (m, msg, end, srcaddr, srcport, dstaddr, dstport, ifid);
     else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, srcaddr, srcport, dstaddr, dstport, ifid);
     else if (QR_OP == UpdQ) mDNSCoreReceiveUpdate  (m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID);
-    else if (QR_OP == UpdR) mDNSCoreReceiveUpdateR (m, msg, end,                                     InterfaceID);
+    else if (QR_OP == UpdR) mDNSCoreReceiveUpdateR (m, msg, end, srcaddr,                            InterfaceID);
     else
     {
         LogMsg("Unknown DNS packet type %02X%02X from %#-15a:%-5d to %#-15a:%-5d length %d on %p (ignored)",
     else
     {
         LogMsg("Unknown DNS packet type %02X%02X from %#-15a:%-5d to %#-15a:%-5d length %d on %p (ignored)",
@@ -8752,6 +10323,7 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *co
 // The main reason for this design is that cache entries point to a *single* question and that question is responsible
 // for keeping the cache fresh as long as it is active. Having multiple active question for a single cache entry
 // breaks this design principle.
 // The main reason for this design is that cache entries point to a *single* question and that question is responsible
 // for keeping the cache fresh as long as it is active. Having multiple active question for a single cache entry
 // breaks this design principle.
+//
 
 // If IsLLQ(Q) is true, it means the question is both:
 // (a) long-lived and
 
 // If IsLLQ(Q) is true, it means the question is both:
 // (a) long-lived and
@@ -8773,9 +10345,12 @@ mDNSlocal DNSQuestion *FindDuplicateQuestion(const mDNS *const m, const DNSQuest
             q->qclass     == question->qclass       &&          // class,
             IsLLQ(q)      == IsLLQ(question)        &&          // and long-lived status matches
             (!q->AuthInfo || question->AuthInfo)    &&          // to avoid deadlock, don't make public query dup of a private one
             q->qclass     == question->qclass       &&          // class,
             IsLLQ(q)      == IsLLQ(question)        &&          // and long-lived status matches
             (!q->AuthInfo || question->AuthInfo)    &&          // to avoid deadlock, don't make public query dup of a private one
+            (q->AnonInfo  == question->AnonInfo)    &&          // Anonymous query not a dup of normal query
             (q->SuppressQuery == question->SuppressQuery) &&    // Questions that are suppressed/not suppressed
             (q->ValidationRequired == question->ValidationRequired) &&  // Questions that require DNSSEC validation
             (q->ValidatingResponse == question->ValidatingResponse) &&  // Questions that are validating responses using DNSSEC
             (q->SuppressQuery == question->SuppressQuery) &&    // Questions that are suppressed/not suppressed
             (q->ValidationRequired == question->ValidationRequired) &&  // Questions that require DNSSEC validation
             (q->ValidatingResponse == question->ValidatingResponse) &&  // Questions that are validating responses using DNSSEC
+            (q->DisallowPID == question->DisallowPID)     &&            // Disallowing a PID should not affect a PID that is allowed
+            (q->BrowseThreshold == question->BrowseThreshold) &&  // browse thresholds must match
             q->qnamehash  == question->qnamehash    &&
             SameDomainName(&q->qname, &question->qname))        // and name
             return(q);
             q->qnamehash  == question->qnamehash    &&
             SameDomainName(&q->qname, &question->qname))        // and name
             return(q);
@@ -8825,6 +10400,11 @@ mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, DNSQuestion *const questi
                 q->triedAllServersOnce = question->triedAllServersOnce;
 
                 q->TargetQID         = question->TargetQID;
                 q->triedAllServersOnce = question->triedAllServersOnce;
 
                 q->TargetQID         = question->TargetQID;
+                if (q->LocalSocket)
+                {
+                    mDNSPlatformUDPClose(q->LocalSocket);
+                }
+                    
                 q->LocalSocket       = question->LocalSocket;
 
                 q->state             = question->state;
                 q->LocalSocket       = question->LocalSocket;
 
                 q->state             = question->state;
@@ -8833,8 +10413,6 @@ mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, DNSQuestion *const questi
                 q->expire            = question->expire;
                 q->ntries            = question->ntries;
                 q->id                = question->id;
                 q->expire            = question->expire;
                 q->ntries            = question->ntries;
                 q->id                = question->id;
-                q->ValidationState  = question->ValidationState;
-                q->ValidationStatus = question->ValidationStatus;
 
                 question->LocalSocket = mDNSNULL;
                 question->nta        = mDNSNULL;    // If we've got a GetZoneData in progress, transfer it to the newly active question
 
                 question->LocalSocket = mDNSNULL;
                 question->nta        = mDNSNULL;    // If we've got a GetZoneData in progress, transfer it to the newly active question
@@ -8872,8 +10450,7 @@ mDNSexport McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname
 
     LogInfo("mDNS_AddMcastResolver: Adding %##s, InterfaceID %p, timeout %u", d->c, interface, timeout);
 
 
     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);
+    mDNS_CheckLock(m);
 
     while (*p)  // Check if we already have this {interface, domain} tuple registered
     {
 
     while (*p)  // Check if we already have this {interface, domain} tuple registered
     {
@@ -9034,10 +10611,49 @@ mDNSexport mDNSBool DomainEnumQuery(const domainname *qname)
     return mDNStrue;
 }
 
     return mDNStrue;
 }
 
+// Note: InterfaceID is the InterfaceID of the question
+mDNSlocal mDNSBool DNSServerMatch(DNSServer *d, mDNSInterfaceID InterfaceID, mDNSs32 ServiceID)
+{
+    // 1) Unscoped questions (NULL InterfaceID) should  consider *only* unscoped DNSServers ( DNSServer
+    // with "scoped" set to kScopeNone)
+    //
+    // 2) Scoped questions (non-NULL InterfaceID) should consider *only* scoped DNSServers (DNSServer
+    // with "scoped" set to kScopeInterfaceId) and their InterfaceIDs should match.
+    //
+    // 3) Scoped questions (non-zero ServiceID) should consider *only* scoped DNSServers (DNSServer
+    // with "scoped" set to kScopeServiceID) and their ServiceIDs should match.
+    //    
+    // The first condition in the "if" statement checks to see if both the question and the DNSServer are
+    // unscoped. The question is unscoped only if InterfaceID is zero and ServiceID is -1.
+    //
+    // If the first condition fails, following are the possible cases (the notes below are using
+    // InterfaceID for discussion and the same holds good for ServiceID):
+    //
+    // - DNSServer is not scoped, InterfaceID is not NULL - we should skip the current DNSServer entry
+    //   as scoped questions should not pick non-scoped DNSServer entry (Refer to (2) above).
+    //
+    // - DNSServer is scoped, InterfaceID is NULL - we should skip the current DNSServer entry as
+    //   unscoped question should not match scoped DNSServer (Refer to (1) above). The InterfaceID check
+    //   would fail in this case.
+    //
+    // - DNSServer is scoped and InterfaceID is not NULL - the InterfaceID of the question and the DNSServer
+    //   should match (Refer to (2) above).
+    //
+    // Note: mDNSInterface_Unicast is used only by .local unicast questions and are treated as unscoped.
+    // If a question is scoped both to InterfaceID and ServiceID, the question will be scoped to InterfaceID.
+
+    if (((d->scoped == kScopeNone) && ((!InterfaceID && ServiceID == -1) || InterfaceID == mDNSInterface_Unicast)) ||
+        ((d->scoped == kScopeInterfaceID) && d->interface == InterfaceID) ||
+        ((d->scoped == kScopeServiceID) && d->serviceID == ServiceID))
+    {
+        return mDNStrue;
+    }
+    return mDNSfalse;
+}
+
 // Sets all the Valid DNS servers for a question
 mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question)
 {
 // Sets all the Valid DNS servers for a question
 mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question)
 {
-    DNSServer *curmatch = mDNSNULL;
     int bestmatchlen = -1, namecount = CountLabels(&question->qname);
     DNSServer *curr;
     int bettermatch, currcount;
     int bestmatchlen = -1, namecount = CountLabels(&question->qname);
     DNSServer *curr;
     int bettermatch, currcount;
@@ -9052,7 +10668,10 @@ mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question)
         debugf("SetValidDNSServers: Parsing DNS server Address %#a (Domain %##s), Scope: %d", &curr->addr, curr->domain.c, curr->scoped);
         // skip servers that will soon be deleted
         if (curr->flags & DNSServer_FlagDelete)
         debugf("SetValidDNSServers: Parsing DNS server Address %#a (Domain %##s), Scope: %d", &curr->addr, curr->domain.c, curr->scoped);
         // skip servers that will soon be deleted
         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; }
+        {
+            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.
 
         // 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.
@@ -9063,12 +10682,13 @@ mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question)
         // 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)
         // 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; }
+        {
+            debugf("SetValidDNSServers: Scoped DNS server %#a (Domain %##s) with Interface Any", &curr->addr, curr->domain.c);
+            continue;
+        }
 
         currcount = CountLabels(&curr->domain);
 
         currcount = CountLabels(&curr->domain);
-        if ((!DEQuery || !curr->cellIntf) &&
-            ((!curr->scoped && (!question->InterfaceID || (question->InterfaceID == mDNSInterface_Unicast))) ||
-             (curr->interface == question->InterfaceID)))
+        if ((!DEQuery || !curr->cellIntf) && DNSServerMatch(curr, question->InterfaceID, question->ServiceID))
         {
             bettermatch = BetterMatchForName(&question->qname, namecount, &curr->domain, currcount, bestmatchlen);
 
         {
             bettermatch = BetterMatchForName(&question->qname, namecount, &curr->domain, currcount, bestmatchlen);
 
@@ -9078,14 +10698,19 @@ mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question)
             // bit
             if ((bettermatch == 1) || (bettermatch == 0))
             {
             // bit
             if ((bettermatch == 1) || (bettermatch == 0))
             {
-                curmatch = curr;
                 bestmatchlen = currcount;
                 bestmatchlen = currcount;
-                if (bettermatch) { debugf("SetValidDNSServers: Resetting all the bits"); question->validDNSServers = zeroOpaque64; timeout = 0; }
+                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;
                 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;
-                if (DEQuery) debugf("DomainEnumQuery: Question %##s, DNSServer %#a, cell %d", question->qname.c, &curr->addr, curr->cellIntf);
+                if (DEQuery)
+                    debugf("DomainEnumQuery: Question %##s, DNSServer %#a, cell %d", question->qname.c, &curr->addr, curr->cellIntf);
                 bit_set_opaque64(question->validDNSServers, index);
             }
         }
                 bit_set_opaque64(question->validDNSServers, index);
             }
         }
@@ -9095,13 +10720,15 @@ mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question)
 
     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));
 
     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 (question->ValidatingResponse ? DEFAULT_UDNSSEC_TIMEOUT : timeout ? timeout : DEFAULT_UDNS_TIMEOUT);
+    // If there are no matching resolvers, then use the default timeout value.
+    // For ProxyQuestion, shorten the timeout so that dig does not timeout on us in case of no response.
+    return ((question->ProxyQuestion || question->ValidatingResponse) ? DEFAULT_UDNSSEC_TIMEOUT : timeout ? timeout : DEFAULT_UDNS_TIMEOUT);
 }
 
 // Get the Best server that matches a name. If you find penalized servers, look for the one
 // that will come out of the penalty box soon
 }
 
 // Get the Best server that matches a name. If you find penalized servers, look for the one
 // that will come out of the penalty box soon
-mDNSlocal DNSServer *GetBestServer(mDNS *m, const domainname *name, mDNSInterfaceID InterfaceID, mDNSOpaque64 validBits, int *selected, mDNSBool nameMatch)
+mDNSlocal DNSServer *GetBestServer(mDNS *m, const domainname *name, mDNSInterfaceID InterfaceID, mDNSs32 ServiceID, mDNSOpaque64 validBits,
+    int *selected, mDNSBool nameMatch)
 {
     DNSServer *curmatch = mDNSNULL;
     int bestmatchlen = -1, namecount = name ? CountLabels(name) : 0;
 {
     DNSServer *curmatch = mDNSNULL;
     int bestmatchlen = -1, namecount = name ? CountLabels(name) : 0;
@@ -9117,10 +10744,18 @@ mDNSlocal DNSServer *GetBestServer(mDNS *m, const domainname *name, mDNSInterfac
     {
         // skip servers that will soon be deleted
         if (curr->flags & DNSServer_FlagDelete)
     {
         // skip servers that will soon be deleted
         if (curr->flags & DNSServer_FlagDelete)
-        { debugf("GetBestServer: Delete set for index %d, DNS server %#a (Domain %##s), scoped %d", index, &curr->addr, curr->domain.c, curr->scoped); continue; }
+        {
+            debugf("GetBestServer: Delete set for index %d, DNS server %#a (Domain %##s), scoped %d", index, &curr->addr, curr->domain.c, curr->scoped);
+            continue;
+        }
 
         // Check if this is a valid DNSServer
 
         // Check if this is a valid DNSServer
-        if (!bit_get_opaque64(validBits, index)) { debugf("GetBestServer: continuing for index %d", index); index++; continue; }
+        if (!bit_get_opaque64(validBits, index))
+        {
+            debugf("GetBestServer: continuing for index %d", index);
+            index++;
+            continue;
+        }
 
         currcount = CountLabels(&curr->domain);
         currPenaltyTime = PenaltyTimeForServer(m, curr);
 
         currcount = CountLabels(&curr->domain);
         currPenaltyTime = PenaltyTimeForServer(m, curr);
@@ -9133,24 +10768,9 @@ mDNSlocal DNSServer *GetBestServer(mDNS *m, const domainname *name, mDNSInterfac
         // the least penalized one. BetterMatchForName walks through all best matches and
         // "currPenaltyTime < bestPenaltyTime" check lets us either pick the first best server
         // in the list when there are no penalized servers and least one among them
         // the least penalized one. BetterMatchForName walks through all best matches and
         // "currPenaltyTime < bestPenaltyTime" check lets us either pick the first best server
         // in the list when there are no penalized servers and least one among them
-        // when there are some penalized servers
-        //
-        // Notes on InterfaceID matching:
-        //
-        // 1) A DNSServer entry may have an InterfaceID but the scoped flag may not be set. This
-        // is the old way of specifying an InterfaceID option for DNSServer. We recoginize these
-        // entries by "scoped" being false. These are like any other unscoped entries except that
-        // if it is picked e.g., domain match, when the packet is sent out later, the packet will
-        // be sent out on that interface. Theese entries can be matched by either specifying a
-        // zero InterfaceID or non-zero InterfaceID on the question. Specifying an InterfaceID on
-        // the question will cause an extra check on matching the InterfaceID on the question
-        // against the DNSServer.
-        //
-        // 2) A DNSServer may also have both scoped set and InterfaceID non-NULL. This
-        // is the new way of specifying an InterfaceID option for DNSServer. These will be considered
-        // only when the question has non-zero interfaceID.
+        // when there are some penalized servers.
 
 
-        if ((!curr->scoped && !InterfaceID) || (curr->interface == InterfaceID))
+        if (DNSServerMatch(curr, InterfaceID, ServiceID))
         {
 
             // If we know that all the names are already equally good matches, then skip calling BetterMatchForName.
         {
 
             // If we know that all the names are already equally good matches, then skip calling BetterMatchForName.
@@ -9158,15 +10778,22 @@ mDNSlocal DNSServer *GetBestServer(mDNS *m, const domainname *name, mDNSInterfac
             // Actually we just need PenaltyTime match, but for the sake of readability we just skip the expensive
             // part and still do some redundant steps e.g., InterfaceID match
 
             // Actually we just need PenaltyTime match, but for the sake of readability we just skip the expensive
             // part and still do some redundant steps e.g., InterfaceID match
 
-            if (nameMatch) bettermatch = BetterMatchForName(name, namecount, &curr->domain, currcount, bestmatchlen);
-            else bettermatch = 0;
+            if (nameMatch)
+                bettermatch = BetterMatchForName(name, namecount, &curr->domain, currcount, bestmatchlen);
+            else
+                bettermatch = 0;
 
             // If we found a better match (bettermatch == 1) then we don't need to
             // compare penalty times. But if we found an equal match, then we compare
             // the penalty times to pick a better match
 
             if ((bettermatch == 1) || ((bettermatch == 0) && currPenaltyTime < bestPenaltyTime))
 
             // If we found a better match (bettermatch == 1) then we don't need to
             // compare penalty times. But if we found an equal match, then we compare
             // the penalty times to pick a better match
 
             if ((bettermatch == 1) || ((bettermatch == 0) && currPenaltyTime < bestPenaltyTime))
-            { currindex = index; curmatch = curr; bestmatchlen = currcount; bestPenaltyTime = currPenaltyTime; }
+            {
+                currindex = index;
+                curmatch = curr;
+                bestmatchlen = currcount;
+                bestPenaltyTime = currPenaltyTime;
+            }
         }
         index++;
     }
         }
         index++;
     }
@@ -9175,7 +10802,7 @@ mDNSlocal DNSServer *GetBestServer(mDNS *m, const domainname *name, mDNSInterfac
 }
 
 // Look up a DNS Server, matching by name and InterfaceID
 }
 
 // Look up a DNS Server, matching by name and InterfaceID
-mDNSexport DNSServer *GetServerForName(mDNS *m, const domainname *name, mDNSInterfaceID InterfaceID)
+mDNSlocal DNSServer *GetServerForName(mDNS *m, const domainname *name, mDNSInterfaceID InterfaceID, mDNSs32 ServiceID)
 {
     DNSServer *curmatch = mDNSNULL;
     char *ifname = mDNSNULL;    // for logging purposes only
 {
     DNSServer *curmatch = mDNSNULL;
     char *ifname = mDNSNULL;    // for logging purposes only
@@ -9189,7 +10816,7 @@ mDNSexport DNSServer *GetServerForName(mDNS *m, const domainname *name, mDNSInte
     // By passing in all ones, we make sure that every DNS server is considered
     allValid.l[0] = allValid.l[1] = 0xFFFFFFFF;
 
     // By passing in all ones, we make sure that every DNS server is considered
     allValid.l[0] = allValid.l[1] = 0xFFFFFFFF;
 
-    curmatch = GetBestServer(m, name, InterfaceID, allValid, mDNSNULL, mDNStrue);
+    curmatch = GetBestServer(m, name, InterfaceID, ServiceID, allValid, mDNSNULL, mDNStrue);
 
     if (curmatch != mDNSNULL)
         LogInfo("GetServerForName: DNS server %#a:%d (Penalty Time Left %d) (Scope %s:%p) found for name %##s", &curmatch->addr,
 
     if (curmatch != mDNSNULL)
         LogInfo("GetServerForName: DNS server %#a:%d (Penalty Time Left %d) (Scope %s:%p) found for name %##s", &curmatch->addr,
@@ -9213,20 +10840,28 @@ mDNSexport DNSServer *GetServerForQuestion(mDNS *m, DNSQuestion *question)
     if ((InterfaceID == mDNSInterface_Unicast) || (InterfaceID == mDNSInterface_LocalOnly))
         InterfaceID = mDNSNULL;
 
     if ((InterfaceID == mDNSInterface_Unicast) || (InterfaceID == mDNSInterface_LocalOnly))
         InterfaceID = mDNSNULL;
 
-    if (InterfaceID) ifname = InterfaceNameForID(m, InterfaceID);
+    if (InterfaceID)
+        ifname = InterfaceNameForID(m, InterfaceID);
 
     if (!mDNSOpaque64IsZero(&question->validDNSServers))
     {
 
     if (!mDNSOpaque64IsZero(&question->validDNSServers))
     {
-        curmatch = GetBestServer(m, name, InterfaceID, question->validDNSServers, &currindex, mDNSfalse);
-        if (currindex != -1) bit_clr_opaque64(question->validDNSServers, currindex);
+        curmatch = GetBestServer(m, name, InterfaceID, question->ServiceID, question->validDNSServers, &currindex, mDNSfalse);
+        if (currindex != -1)
+            bit_clr_opaque64(question->validDNSServers, currindex);
     }
 
     if (curmatch != mDNSNULL)
     }
 
     if (curmatch != mDNSNULL)
-        LogInfo("GetServerForQuestion: %p DNS server %#a:%d (Penalty Time Left %d) (Scope %s:%p) found for name %##s (%s)", question, &curmatch->addr,
-                mDNSVal16(curmatch->port), (curmatch->penaltyTime ? (curmatch->penaltyTime - m->timenow) : 0), ifname ? ifname : "None",
-                InterfaceID, name, DNSTypeName(question->qtype));
+    {
+        LogInfo("GetServerForQuestion: %p DNS server (%p) %#a:%d (Penalty Time Left %d) (Scope %s:%p:%d) found for name %##s (%s)",
+                question, curmatch, &curmatch->addr, mDNSVal16(curmatch->port),
+                (curmatch->penaltyTime ? (curmatch->penaltyTime - m->timenow) : 0), ifname ? ifname : "None",
+                InterfaceID, question->ServiceID, name, DNSTypeName(question->qtype));
+    }
     else
     else
-        LogInfo("GetServerForQuestion: %p no DNS server (Scope %s:%p) found for name %##s (%s)", question, ifname ? ifname : "None", InterfaceID, name, DNSTypeName(question->qtype));
+    {
+        LogInfo("GetServerForQuestion: %p no DNS server (Scope %s:%p:%d) found for name %##s (%s)",
+            question, ifname ? ifname : "None", InterfaceID, question->ServiceID, name, DNSTypeName(question->qtype));
+    }
 
     return(curmatch);
 }
 
     return(curmatch);
 }
@@ -9239,9 +10874,9 @@ mDNSexport DNSServer *GetServerForQuestion(mDNS *m, DNSQuestion *question)
 mDNSlocal void LLQNATCallback(mDNS *m, NATTraversalInfo *n)
 {
     DNSQuestion *q;
 mDNSlocal void LLQNATCallback(mDNS *m, NATTraversalInfo *n)
 {
     DNSQuestion *q;
-    (void)n;    // Unused
     mDNS_Lock(m);
     LogInfo("LLQNATCallback external address:port %.4a:%u, NAT result %d", &n->ExternalAddress, mDNSVal16(n->ExternalPort), n->Result);
     mDNS_Lock(m);
     LogInfo("LLQNATCallback external address:port %.4a:%u, NAT result %d", &n->ExternalAddress, mDNSVal16(n->ExternalPort), n->Result);
+    n->clientContext = mDNSNULL; // we received at least one callback since starting this NAT-T
     for (q = m->Questions; q; q=q->next)
         if (ActiveQuestion(q) && !mDNSOpaque16IsZero(q->TargetQID) && q->LongLived)
             startLLQHandshake(m, q);    // If ExternalPort is zero, will do StartLLQPolling instead
     for (q = m->Questions; q; q=q->next)
         if (ActiveQuestion(q) && !mDNSOpaque16IsZero(q->TargetQID) && q->LongLived)
             startLLQHandshake(m, q);    // If ExternalPort is zero, will do StartLLQPolling instead
@@ -9251,80 +10886,147 @@ mDNSlocal void LLQNATCallback(mDNS *m, NATTraversalInfo *n)
     mDNS_Unlock(m);
 }
 
     mDNS_Unlock(m);
 }
 
-mDNSlocal mDNSBool IsAutoTunnelAddress(mDNS *const m, const mDNSv6Addr a)
+mDNSlocal mDNSBool IsPrivateDomain(mDNS *const m, DNSQuestion *q)
 {
 {
-    DomainAuthInfo *ai = mDNSNULL;
-    
-    if (mDNSSameIPv6Address(a, m->AutoTunnelRelayAddr))
+    DomainAuthInfo *AuthInfo;
+    // Skip Private domains as we have special addresses to get the hosts in the Private domain
+    AuthInfo = GetAuthInfoForName_internal(m, &q->qname);
+    if (AuthInfo && !AuthInfo->deltime && AuthInfo->AutoTunnel)
+    {
+        debugf("IsPrivateDomain: %##s true", q->qname.c);
         return mDNStrue;
         return mDNStrue;
-    
-    for (ai = m->AuthInfoList; ai; ai = ai->next)
+    }
+    else
     {
     {
-        if (!ai->deltime && ai->AutoTunnel && mDNSSameIPv6Address(a, ai->AutoTunnelInnerAddress))
-        {
-            return mDNStrue;
-        }
+        debugf("IsPrivateDomain: %##s false", q->qname.c);
+        return mDNSfalse;
     }
     }
-    
-    return mDNSfalse;
 }
 
 }
 
-mDNSlocal mDNSBool ShouldSuppressQuery(mDNS *const m, domainname *qname, mDNSu16 qtype, mDNSInterfaceID InterfaceID)
+// This function takes the DNSServer as a separate argument because sometimes the
+// caller has not yet assigned the DNSServer, but wants to evaluate the SuppressQuery
+// status before switching to it.
+mDNSlocal mDNSBool ShouldSuppressUnicastQuery(mDNS *const m, DNSQuestion *q, DNSServer *d)
 {
 {
-    NetworkInterfaceInfo *i;
-    mDNSs32 iptype;
-    DomainAuthInfo *AuthInfo;
+    // Some callers don't check for the qtype
+    if (q->qtype != kDNSType_A && q->qtype != kDNSType_AAAA)
+    {
+        LogInfo("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, not A/AAAA type", q->qname.c, DNSTypeName(q->qtype));
+        return mDNSfalse;
+    }
 
 
-    if (qtype == kDNSType_A) iptype = mDNSAddrType_IPv4;
-    else if (qtype == kDNSType_AAAA) iptype = mDNSAddrType_IPv6;
-    else { LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, not A/AAAA type", qname, DNSTypeName(qtype)); return mDNSfalse; }
+    // Private domains are exempted irrespective of what the DNSServer says
+    if (IsPrivateDomain(m, q))
+    {
+        LogInfo("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, Private Domain", q->qname.c, DNSTypeName(q->qtype));
+        return mDNSfalse;
+    }
 
 
-    // We still want the ability to be able to listen to the local services and hence
-    // don't fail .local requests. We always have a loopback interface which we don't
-    // check here.
-    if (InterfaceID != mDNSInterface_Unicast && IsLocalDomain(qname)) { LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Local question", qname, DNSTypeName(qtype)); return mDNSfalse; }
+    if (!d)
+    {
+        LogInfo("ShouldSuppressUnicastQuery: Query suppressed for %##s, qtype %s, as the DNS server is NULL", q->qname.c, DNSTypeName(q->qtype));
+        return mDNStrue;
+    }
+    
+    // Check if the DNS Configuration allows A/AAAA queries to be sent
+    if ((q->qtype == kDNSType_A) && (d->req_A))
+    {
+        LogInfo("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, DNSServer %##s %#a:%d allows A queries", q->qname.c,
+                DNSTypeName(q->qtype), d->domain.c, &d->addr, mDNSVal16(d->port));
+        return mDNSfalse;
+    }
+    if ((q->qtype == kDNSType_AAAA) && (d->req_AAAA))
+    {
+        LogInfo("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, DNSServer %##s %#a:%d allows AAAA queries", q->qname.c,
+                DNSTypeName(q->qtype), d->domain.c, &d->addr, mDNSVal16(d->port));
+        return mDNSfalse;
+    }
 
 
-    // Skip Private domains as we have special addresses to get the hosts in the Private domain
-    AuthInfo = GetAuthInfoForName_internal(m, qname);
-    if (AuthInfo && !AuthInfo->deltime && AuthInfo->AutoTunnel)
-    { LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Private Domain", qname, DNSTypeName(qtype)); return mDNSfalse; }
+    LogInfo("ShouldSuppressUnicastQuery: Query suppressed for %##s, qtype %s, since DNS Configuration does not allow (req_A is %s and req_AAAA is %s)",
+        q->qname.c, DNSTypeName(q->qtype), d->req_A ? "true" : "false", d->req_AAAA ? "true" : "false");
 
 
-    // Match on Type, Address and InterfaceID
+    return mDNStrue;
+}
+
+mDNSlocal mDNSBool ShouldSuppressDotLocalQuery(mDNS *const m, DNSQuestion *q)
+{
+    NetworkInterfaceInfo *intf;
+    AuthRecord *rr;
+    mDNSBool ret;
+
+    // Check to see if there is at least one interface other than loopback and don't suppress
+    // .local questions if you find one. If we have at least one interface, it means that
+    // we can send unicast queries for the .local name and we don't want to suppress
+    // multicast in that case as upper layers don't know how to handle if we return a 
+    // negative response for multicast followed by a positive response for unicast.
     //
     //
-    // Check whether we are looking for a name that ends in .local, then presence of a link-local
-    // address on the interface is sufficient.
-    for (i = m->HostInterfaces; i; i = i->next)
+    // Note: we used to check for multicast capable interfaces instead of just any interface
+    // present. That did not work in the case where we have a valid interface for unicast
+    // but not multicast capable e.g., cellular, as we ended up delivering a negative response 
+    // first and the upper layer did not wait for the positive response that came later.
+    for (intf = m->HostInterfaces; intf; intf = intf->next)
     {
     {
-        if (i->ip.type != iptype) continue;
+        if (intf->InterfaceActive && !intf->Loopback)
+        {
+            LogInfo("ShouldSuppressDotLocalQuery: Found interface %s, not suppressing", intf->ifname);
+            return mDNSfalse;
+        }
+    }
 
 
-        if (!InterfaceID || (InterfaceID == mDNSInterface_LocalOnly) || (InterfaceID == mDNSInterface_P2P) ||
-            (InterfaceID == mDNSInterface_Unicast) || (i->InterfaceID == InterfaceID))
+    // 1. If we find a LocalOnly or P2P record answering this question, then don't suppress it.
+    //    Set m->CurrentQuestion as it is required by AnswerQuestionWithLORecord.
+    m->CurrentQuestion = q;
+    ret = AnswerQuestionWithLORecord(m, q, mDNStrue);
+    m->CurrentQuestion = mDNSNULL;
+
+    if (ret)
+    {
+        LogInfo("ShouldSuppressDotLocalQuery: Found LocalOnly record for %##s (%s), not suppressing", q->qname.c,
+            DNSTypeName(q->qtype));
+        return mDNSfalse;
+    }
+
+    // 2. If we find a local AuthRecord answering this question, then don't suppress it. 
+    for (rr = m->ResourceRecords; rr; rr = rr->next)
+    {
+        if (ResourceRecordAnswersQuestion(&rr->resrec, q))
         {
         {
-            if (iptype == mDNSAddrType_IPv4 && !mDNSv4AddressIsLoopback(&i->ip.ip.v4) && !mDNSv4AddressIsLinkLocal(&i->ip.ip.v4))
-            {
-                LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Local Address %.4a found", qname, DNSTypeName(qtype),
-                        &i->ip.ip.v4);
-                if (m->SleepState == SleepState_Sleeping)
-                                       LogInfo("ShouldSuppressQuery: Would have returned true earlier");
-                return mDNSfalse;
-            }
-            else if (iptype == mDNSAddrType_IPv6 &&
-                     !mDNSv6AddressIsLoopback(&i->ip.ip.v6) &&
-                     !mDNSv6AddressIsLinkLocal(&i->ip.ip.v6) &&
-                     !IsAutoTunnelAddress(m, i->ip.ip.v6))
-            {
-                LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Local Address %.16a found", qname, DNSTypeName(qtype),
-                        &i->ip.ip.v6);
-                if (m->SleepState == SleepState_Sleeping)
-                                       LogInfo("ShouldSuppressQuery: Would have returned true earlier");
-                return mDNSfalse;
-            }
+            LogInfo("ShouldSuppressDotLocalQuery: Found resource record %s for %##s (%s) not suppressing", ARDisplayString(m, rr),
+                q->qname.c, DNSTypeName(q->qtype));
+            return mDNSfalse;
         }
     }
         }
     }
-    LogInfo("ShouldSuppressQuery: Query suppressed for %##s, qtype %s, because no matching interface found", qname, DNSTypeName(qtype));
     return mDNStrue;
 }
 
     return mDNStrue;
 }
 
+mDNSlocal mDNSBool ShouldSuppressQuery(mDNS *const m, DNSQuestion *q)
+{
+    if (q->qtype != kDNSType_A && q->qtype != kDNSType_AAAA)
+    {
+        LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, not A/AAAA type", q->qname.c, DNSTypeName(q->qtype));
+        return mDNSfalse;
+    }
+
+    // We still want the ability to be able to listen to the local services and hence
+    // don't fail .local query if we have local records that can potentially answer
+    // the question.
+    if (q->InterfaceID != mDNSInterface_Unicast && IsLocalDomain(&q->qname))
+    {
+        if (!ShouldSuppressDotLocalQuery(m, q))
+        {
+            LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Local question", q->qname.c, DNSTypeName(q->qtype));
+            return mDNSfalse;
+        }
+        else
+        {
+            LogInfo("ShouldSuppressQuery: Query suppressed for %##s, qtype %s, Local question", q->qname.c, DNSTypeName(q->qtype));
+            return mDNStrue;
+        }
+    }
+
+    return (ShouldSuppressUnicastQuery(m, q, q->qDNSServer));
+}
+
 mDNSlocal void CacheRecordRmvEventsForCurrentQuestion(mDNS *const m, DNSQuestion *q)
 {
     CacheRecord *rr;
 mDNSlocal void CacheRecordRmvEventsForCurrentQuestion(mDNS *const m, DNSQuestion *q)
 {
     CacheRecord *rr;
@@ -9408,7 +11110,7 @@ mDNSlocal mDNSBool LocalRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q
     {
         for (rr = ag->members; rr; rr=rr->next)
             // Filter the /etc/hosts records - LocalOnly, Unique, A/AAAA/CNAME
     {
         for (rr = ag->members; rr; rr=rr->next)
             // Filter the /etc/hosts records - LocalOnly, Unique, A/AAAA/CNAME
-            if (LORecordAnswersAddressType(rr) && LocalOnlyRecordAnswersQuestion(rr, q))
+            if (UniqueLocalOnlyRecord(rr) && LocalOnlyRecordAnswersQuestion(rr, q))
             {
                 LogInfo("LocalRecordRmvEventsForQuestion: Delivering possible Rmv events with record %s",
                         ARDisplayString(m, rr));
             {
                 LogInfo("LocalRecordRmvEventsForQuestion: Delivering possible Rmv events with record %s",
                         ARDisplayString(m, rr));
@@ -9448,6 +11150,65 @@ mDNSlocal mDNSBool CacheRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q
     return mDNStrue;
 }
 
     return mDNStrue;
 }
 
+mDNSlocal void SuppressStatusChanged(mDNS *const m, DNSQuestion *q, DNSQuestion **restart)
+{
+    // 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)
+    {
+        q->SuppressQuery = mDNSfalse;
+        if (!CacheRecordRmvEventsForQuestion(m, q))
+        {
+            LogInfo("SuppressStatusChanged: Question deleted while delivering RMV events from cache");
+            return;
+        }
+        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("SuppressStatusChanged: Question deleted while delivering RMV events from Local AuthRecords");
+        return;
+    }
+
+    // There are two cases here.
+    //
+    // 1. Previously it was suppressed and now it is not suppressed, restart the question so
+    // that it will start as a new question. Note that we can't just call ActivateUnicastQuery
+    // because when we get the response, if we had entries in the cache already, it will not answer
+    // this question if the cache entry did not change. Hence, we need to restart
+    // the query so that it can be answered from the cache.
+    //
+    // 2. Previously it was not suppressed and now it is suppressed. We need to restart the questions
+    // so that we redo the duplicate checks in mDNS_StartQuery_internal. A SuppressUnusable question
+    // 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.
+    // This affects both efficiency and also the current design where there is only one active question
+    // pointed to from a cache entry.
+    //
+    // We restart queries in a two step process by first calling stop and build a temporary list which we
+    // will restart at the end. The main reason for the two step process is to handle duplicate questions.
+    // If there are duplicate questions, calling stop inherits the values from another question on the list (which
+    // will soon become the real question) including q->ThisQInterval which might be zero if it was
+    // suppressed before. At the end when we have restarted all questions, none of them is active as each
+    // inherits from one another and we need to reactivate one of the questions here which is a little hacky.
+    //
+    // It is much cleaner and less error prone to build a list of questions and restart at the end.
+
+    LogInfo("SuppressStatusChanged: Stop question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype));
+    mDNS_StopQuery_internal(m, q);
+    q->next = *restart;
+    *restart = q;
+}
+
 // The caller should hold the lock
 mDNSexport void CheckSuppressUnusableQuestions(mDNS *const m)
 {
 // The caller should hold the lock
 mDNSexport void CheckSuppressUnusableQuestions(mDNS *const m)
 {
@@ -9473,304 +11234,480 @@ mDNSexport void CheckSuppressUnusableQuestions(mDNS *const m)
     {
         q = m->RestartQuestion;
         m->RestartQuestion = q->next;
     {
         q = m->RestartQuestion;
         m->RestartQuestion = q->next;
-        if (!mDNSOpaque16IsZero(q->TargetQID) && q->SuppressUnusable)
+        if (q->SuppressUnusable)
         {
             mDNSBool old = q->SuppressQuery;
         {
             mDNSBool old = q->SuppressQuery;
-            q->SuppressQuery = ShouldSuppressQuery(m, &q->qname, q->qtype, q->InterfaceID);
+            q->SuppressQuery = ShouldSuppressQuery(m, q);
             if (q->SuppressQuery != old)
             {
             if (q->SuppressQuery != old)
             {
-                // 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)
+                // 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
+                SuppressStatusChanged(m, q, &restart);
+            }
+        }
+    }
+    while (restart)
+    {
+        q = restart;
+        restart = restart->next;
+        q->next = mDNSNULL;
+        LogInfo("CheckSuppressUnusableQuestions: Start question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype));
+        mDNS_StartQuery_internal(m, q);
+    }
+}
 
 
-                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;
-                }
+mDNSlocal void RestartUnicastQuestions(mDNS *const m)
+{
+    DNSQuestion *q;
+    DNSQuestion *restart = mDNSNULL;
+
+    if (m->RestartQuestion)
+        LogMsg("RestartUnicastQuestions: 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 (q->Restart)
+        {
+            if (mDNSOpaque16IsZero(q->TargetQID))
+                LogMsg("RestartUnicastQuestions: ERROR!! Restart set for multicast question %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+                
+            q->Restart = 0;
+            SuppressStatusChanged(m, q, &restart);
+        }
+    }
+    while (restart)
+    {
+        q = restart;
+        restart = restart->next;
+        q->next = mDNSNULL;
+        LogInfo("RestartUnicastQuestions: Start question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype));
+        mDNS_StartQuery_internal(m, q);
+    }
+}
 
 
-                // 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.
-                //
-                // 1. Previously it was suppressed and now it is not suppressed, restart the question so
-                // that it will start as a new question. Note that we can't just call ActivateUnicastQuery
-                // because when we get the response, if we had entries in the cache already, it will not answer
-                // this question if the cache entry did not change. Hence, we need to restart
-                // the query so that it can be answered from the cache.
-                //
-                // 2. Previously it was not suppressed and now it is suppressed. We need to restart the questions
-                // so that we redo the duplicate checks in mDNS_StartQuery_internal. A SuppressUnusable question
-                // 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.
-                // This affects both efficiency and also the current design where there is only one active question
-                // pointed to from a cache entry.
-                //
-                // We restart queries in a two step process by first calling stop and build a temporary list which we
-                // will restart at the end. The main reason for the two step process is to handle duplicate questions.
-                // If there are duplicate questions, calling stop inherits the values from another question on the list (which
-                // will soon become the real question) including q->ThisQInterval which might be zero if it was
-                // suppressed before. At the end when we have restarted all questions, none of them is active as each
-                // inherits from one another and we need to reactivate one of the questions here which is a little hacky.
-                //
-                // It is much cleaner and less error prone to build a list of questions and restart at the end.
+// ValidateParameters() is called by mDNS_StartQuery_internal() to check the client parameters of 
+// DNS Question that are already set by the client before calling mDNS_StartQuery() 
+mDNSlocal mStatus ValidateParameters(mDNS *const m, DNSQuestion *const question)
+{
 
 
-                LogInfo("CheckSuppressUnusableQuestions: Stop question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype));
-                mDNS_StopQuery_internal(m, q);
-                q->next = restart;
-                restart = q;
-            }
+    if (question->Target.type && !ValidQuestionTarget(question))
+    {
+        LogMsg("ValidateParameters: 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;
+    }
+
+    // If no question->Target specified, clear TargetPort
+    if (!question->Target.type) 
+        question->TargetPort = zeroIPPort;
+
+    if (!ValidateDomainName(&question->qname))
+    {
+        LogMsg("ValidateParameters: Attempt to start query with invalid qname %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
+        return(mStatus_Invalid);
+    }
+
+    // If this question is referencing a specific interface, verify it exists 
+    if (question->InterfaceID && question->InterfaceID != mDNSInterface_LocalOnly && question->InterfaceID != mDNSInterface_Unicast && question->InterfaceID != mDNSInterface_P2P)
+    {
+        NetworkInterfaceInfo *intf = FirstInterfaceForID(m, question->InterfaceID);
+        if (!intf)
+            LogMsg("ValidateParameters: Note: InterfaceID %p for question %##s (%s) not currently found in active interface list",
+                    question->InterfaceID, question->qname.c, DNSTypeName(question->qtype));
+    }
+    
+    return(mStatus_NoError);
+}
+
+// InitDNSConfig() is called by InitCommonState() to initialize the DNS configuration of the Question.    
+// These are a subset of the internal uDNS fields. Must be done before ShouldSuppressQuery() & mDNS_PurgeForQuestion()
+mDNSlocal void InitDNSConfig(mDNS *const m, DNSQuestion *const question)
+{
+    // First reset all DNS Configuration
+    question->qDNSServer          = mDNSNULL;
+    question->validDNSServers     = zeroOpaque64;
+    question->triedAllServersOnce = 0;                                                                                                                                
+    question->noServerResponse    = 0;  
+    question->StopTime            = 0;
+
+    // Need not initialize the DNS Configuration for Local Only OR P2P Questions
+    if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P)
+        return;
+    // Proceed to initialize DNS Configuration (some are set in SetValidDNSServers())
+    if (!mDNSOpaque16IsZero(question->TargetQID))
+    {     
+        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.
+        //
+        // Note that we set the timeout for all questions. If this turns out to be a duplicate, 
+        // it gets a full timeout value even if the original question times out earlier.
+        if (question->TimeoutQuestion)
+        {
+            question->StopTime = NonZeroTime(m->timenow + timeout * mDNSPlatformOneSecond);
+            LogInfo("InitDNSConfig: Setting StopTime on question %p %##s (%s)", question, question->qname.c, DNSTypeName(question->qtype));
         }
         }
+
+        question->qDNSServer = GetServerForQuestion(m, question);
+        LogInfo("InitDNSConfig: 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));
+    }
+    else
+    {     
+        if (question->TimeoutQuestion)
+            question->StopTime = NonZeroTime(m->timenow + GetTimeoutForMcastQuestion(m, question) * mDNSPlatformOneSecond);
+    }
+    // Set StopTime here since it is a part of DNS Configuration 
+    if (question->StopTime)
+        SetNextQueryStopTime(m, question);
+    // SetNextQueryTime() need not be initialized for LocalOnly OR P2P Questions since those questions
+    // will never be transmitted on the wire. Hence we call SetNextQueryTime() here.
+    SetNextQueryTime(m,question); 
+}
+
+// InitCommonState() is called by mDNS_StartQuery_internal() to initialize the common(uDNS/mDNS) internal
+// state fields of the DNS Question. These are independent of the Client layer.
+mDNSlocal mDNSBool InitCommonState(mDNS *const m, DNSQuestion *const question)
+{
+    mDNSBool purge;
+    int i;
+
+    // Note: In the case where we already have the answer to this question in our cache, that may be all the client
+    // wanted, and they may immediately cancel their question. In this case, sending an actual query on the wire would
+    // be a waste. For that reason, we schedule our first query to go out in half a second (InitialQuestionInterval).
+    // If AnswerNewQuestion() finds that we have *no* relevant answers currently in our cache, then it will accelerate
+    // that to go out immediately.
+    question->next              = mDNSNULL;
+    // ThisQInterval should be initialized before any memory allocations occur. If malloc
+    // debugging is turned on within mDNSResponder (see mDNSDebug.h for details) it validates
+    // the question list to check if ThisQInterval is negative which means the question has been
+    // stopped and can't be on the list. The question is already on the list and ThisQInterval
+    // can be negative if the caller just stopped it and starting it again. Hence, it always has to
+    // be initialized. CheckForSoonToExpireRecords below prints the cache records when logging is
+    // turned ON which can allocate memory e.g., base64 encoding, in the case of DNSSEC.
+    question->ThisQInterval     = InitialQuestionInterval;                  // MUST be > zero for an active question
+    question->qnamehash         = DomainNameHashValue(&question->qname);
+    question->DelayAnswering    = CheckForSoonToExpireRecords(m, &question->qname, question->qnamehash, HashSlot(&question->qname), &purge);
+    question->LastQTime         = m->timenow;
+    question->ExpectUnicastResp = 0;
+    question->LastAnswerPktNum  = m->PktNum;
+    question->RecentAnswerPkts  = 0;
+    question->CurrentAnswers    = 0;
+
+#if APPLE_OSX_mDNSResponder
+
+// Initial browse threshold used by Finder.
+#define mDNSFinderBrowseThreshold 20
+
+    // Set the threshold at which we move to a passive browse state,
+    // not actively sending queries.
+    if (question->flags & kDNSServiceFlagsThresholdOne)
+        question->BrowseThreshold   = 1;
+    else if (question->flags & kDNSServiceFlagsThresholdFinder)
+        question->BrowseThreshold   = mDNSFinderBrowseThreshold;
+    else
+        question->BrowseThreshold   = 0;
+
+#else   // APPLE_OSX_mDNSResponder
+   question->BrowseThreshold   = 0;
+#endif  // APPLE_OSX_mDNSResponder
+    question->CachedAnswerNeedsUpdate = mDNSfalse;
+
+    question->LargeAnswers      = 0;
+    question->UniqueAnswers     = 0;
+    question->LOAddressAnswers  = 0;
+    question->FlappingInterface1 = mDNSNULL;
+    question->FlappingInterface2 = mDNSNULL;
+
+    question->ServiceID = mDNSPlatformGetServiceID(m, question);
+    
+    InitDNSConfig(m, question);
+
+    question->AuthInfo          = GetAuthInfoForQuestion(m, question);
+    question->SuppressQuery     = 0;
+    if (question->SuppressUnusable)
+        question->SuppressQuery = ShouldSuppressQuery(m, question);
+
+    // If ServiceID is 0 or the policy disallows making DNS requests,
+    // set DisallowPID
+    question->DisallowPID       = (question->ServiceID == 0 || (mDNSPlatformAllowPID(m, question) == 0));
+    if (question->DisallowPID)
+    {
+        LogInfo("InitCommonState: Query suppressed for %##s (%s), PID %d/ServiceID %d not allowed", question->qname.c,
+            DNSTypeName(question->qtype), question->pid, question->ServiceID);
+    }
+
+    question->NextInDQList      = mDNSNULL;
+    question->SendQNow          = mDNSNULL;
+    question->SendOnAll         = mDNSfalse;
+
+#if mDNS_REQUEST_UNICAST_RESPONSE
+    question->RequestUnicast    = SET_QU_IN_FIRST_FOUR_QUERIES;
+#else   // mDNS_REQUEST_UNICAST_RESPONSE
+    question->RequestUnicast    = SET_QU_IN_FIRST_QUERY;
+#endif  // mDNS_REQUEST_UNICAST_RESPONSE
+
+#if APPLE_OSX_mDNSResponder
+    // Request unicast response for first 4 queries to increase
+    // reliability in an environment with high multicast packet loss.
+    // Must set to one more than the number of unicast queries you want, since SendQueries()
+    // decrements it before calling BuildQuestion() which acts on it.
+    if (question->flags & kDNSServiceFlagsUnicastResponse)
+    {
+        question->RequestUnicast    = SET_QU_IN_FIRST_FOUR_QUERIES;
+        LogInfo("InitCommonState: setting RequestUnicast = %d for %##s (%s)", question->RequestUnicast, question->qname.c,
+            DNSTypeName(question->qtype));
+    } 
+    else if (question->flags & kDNSServiceFlagsThresholdFinder)
+    {
+        // always send one request with QU bit set when kDNSServiceFlagsThresholdFinder is set
+#if mDNS_REQUEST_UNICAST_RESPONSE
+        question->RequestUnicast    = SET_QU_IN_FIRST_FOUR_QUERIES;
+#else   // mDNS_REQUEST_UNICAST_RESPONSE
+        question->RequestUnicast    = SET_QU_IN_FIRST_QUERY;
+#endif  // mDNS_REQUEST_UNICAST_RESPONSE
+
+        LogInfo("InitCommonState: kDNSServiceFlagsThresholdFinder set, setting RequestUnicast = %d for %##s (%s)", 
+            question->RequestUnicast, question->qname.c, DNSTypeName(question->qtype));
+    }
+#endif  // APPLE_OSX_mDNSResponder
+
+    question->LastQTxTime       = m->timenow;
+    question->CNAMEReferrals    = 0; 
+
+    question->WakeOnResolveCount = 0;
+    if (question->WakeOnResolve)
+    {     
+        question->WakeOnResolveCount = InitialWakeOnResolveCount;
+        purge = mDNStrue;
+    }     
+
+    for (i=0; i<DupSuppressInfoSize; i++)
+        question->DupSuppress[i].InterfaceID = mDNSNULL;
+
+    question->Restart = 0;
+
+    debugf("InitCommonState: Question %##s (%s) Interface %p Now %d Send in %d Answer in %d (%p) %s (%p)",
+            question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, m->timenow,
+            NextQSendTime(question) - m->timenow,
+            question->DelayAnswering ? question->DelayAnswering - m->timenow : 0,
+            question, question->DuplicateOf ? "duplicate of" : "not duplicate", question->DuplicateOf);
+
+    if (question->DelayAnswering)
+        LogInfo("InitCommonState: Delaying answering for %d ticks while cache stabilizes for %##s (%s)",
+                 question->DelayAnswering - m->timenow, question->qname.c, DNSTypeName(question->qtype));
+
+    return(purge);
+}
+
+// Excludes the DNS Config fields which are already handled by InitDNSConfig()
+mDNSlocal void InitWABState(DNSQuestion *const question)
+{
+    // We'll create our question->LocalSocket on demand, if needed.
+    // We won't need one for duplicate questions, or from questions answered immediately out of the cache.
+    // We also don't need one for LLQs because (when we're using NAT) we want them all to share a single
+    // NAT mapping for receiving inbound add/remove events.
+    question->LocalSocket       = mDNSNULL; 
+    question->unansweredQueries = 0;
+    question->nta               = mDNSNULL;
+    question->servAddr          = zeroAddr;
+    question->servPort          = zeroIPPort;
+    question->tcp               = mDNSNULL;
+    question->NoAnswer          = NoAnswer_Normal;
+}
+
+mDNSlocal void InitLLQNATState(mDNS *const m)
+{
+    // If we don't have our NAT mapping active, start it now
+    if (!m->LLQNAT.clientCallback)
+    {
+        m->LLQNAT.Protocol       = NATOp_MapUDP;
+        m->LLQNAT.IntPort        = m->UnicastPort4;
+        m->LLQNAT.RequestedPort  = m->UnicastPort4;
+        m->LLQNAT.clientCallback = LLQNATCallback;
+        m->LLQNAT.clientContext  = (void*)1; // Means LLQ NAT Traversal just started
+        mDNS_StartNATOperation_internal(m, &m->LLQNAT);
+    }
+}
+
+mDNSlocal void InitLLQState(DNSQuestion *const question)
+{
+    question->state             = LLQ_InitialRequest;
+    question->ReqLease          = 0;
+    question->expire            = 0;
+    question->ntries            = 0;
+    question->id                = zeroOpaque64;
+}
+
+// InitDNSSECProxyState() is called by mDNS_StartQuery_internal() to initialize
+// DNSSEC & DNS Proxy fields of the DNS Question. 
+mDNSlocal void InitDNSSECProxyState(mDNS *const m, DNSQuestion *const question)
+{
+    (void) m;
+
+    // DNS server selection affects DNSSEC. Turn off validation if req_DO is not set
+    // or the request is going over cellular interface.
+    //
+    // Note: This needs to be done here before we call FindDuplicateQuestion as it looks
+    // at ValidationRequired setting also.
+    if (question->qDNSServer)
+    {
+        if (question->qDNSServer->cellIntf)
+        {
+            LogInfo("InitDNSSECProxyState: Turning off validation for %##s (%s); going over cell", question->qname.c, DNSTypeName(question->qtype));
+            question->ValidationRequired = mDNSfalse;
+        }
+        if (DNSSECOptionalQuestion(question) && !(question->qDNSServer->req_DO))
+        {
+            LogInfo("InitDNSSECProxyState: Turning off validation for %##s (%s); req_DO false",
+                question->qname.c, DNSTypeName(question->qtype));
+            question->ValidationRequired = DNSSEC_VALIDATION_NONE;
+        }
+    }
+    question->ValidationState = (question->ValidationRequired ? DNSSECValRequired : DNSSECValNotRequired);
+    question->ValidationStatus = 0;
+    question->responseFlags    = zeroID;
+}
+
+// Once the question is completely initialized including the duplicate logic, this function
+// is called to finalize the unicast question which requires flushing the cache if needed,
+// activating the query etc.
+mDNSlocal void FinalizeUnicastQuestion(mDNS *const m, DNSQuestion *question, mDNSBool purge)
+{
+    // Ensure DNS related info of duplicate question is same as the orig question
+    if (question->DuplicateOf)
+    {
+        question->validDNSServers = question->DuplicateOf->validDNSServers;
+        question->qDNSServer = question->DuplicateOf->qDNSServer;
+        LogInfo("FinalizeUnicastQuestion: Duplicate question %p (%p) %##s (%s), DNS Server %#a:%d",
+                 question, question->DuplicateOf, question->qname.c, DNSTypeName(question->qtype),
+                 question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL,
+                 mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort));
+    }
+
+    ActivateUnicastQuery(m, question, mDNSfalse);
+
+    // If purge was set above, flush the cache. Need to do this after we set the
+    // DNS server on the question
+    if (purge)
+    {
+        question->DelayAnswering = 0;
+        mDNS_PurgeForQuestion(m, question);
     }
     }
-    while (restart)
+    else if (!question->DuplicateOf && DNSSECQuestion(question))
     {
     {
-        q = restart;
-        restart = restart->next;
-        q->next = mDNSNULL;
-        LogInfo("CheckSuppressUnusableQuestions: Start question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype));
-        mDNS_StartQuery_internal(m, q);
+        // For DNSSEC questions, we need to have the RRSIGs also for verification.
+        CheckForDNSSECRecords(m, question);
+    }
+    if (question->LongLived)
+    {
+        // Unlike other initializations, InitLLQNATState should be done after
+        // we determine that it is a unicast question.  LongLived is set for
+        // both multicast and unicast browse questions but we should initialize
+        // the LLQ NAT state only for unicast. Otherwise we will unnecessarily
+        // start the NAT traversal that is not needed.
+        InitLLQNATState(m);
+#if APPLE_OSX_mDNSResponder
+        UpdateAutoTunnelDomainStatuses(m);
+#endif
     }
 }
 
 mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question)
 {
     }
 }
 
 mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question)
 {
-    if (question->Target.type && !ValidQuestionTarget(question))
-    {
-        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;
-    }
+    DNSQuestion **q;
+    mStatus vStatus;
+    mDNSBool purge;
 
 
-    if (!question->Target.type) question->TargetPort = zeroIPPort;  // If no question->Target specified clear TargetPort
+    // First check for cache space (can't do queries if there is no cache space allocated)
+    if (m->rrcache_size == 0)
+        return(mStatus_NoCache);
 
 
+    vStatus = ValidateParameters(m, question);
+    if (vStatus)
+        return(vStatus);
+    
     question->TargetQID =
 #ifndef UNICAST_DISABLED
     question->TargetQID =
 #ifndef UNICAST_DISABLED
-            (question->Target.type || Question_uDNS(question)) ? mDNS_NewMessageID(m) :
+                          (question->Target.type || Question_uDNS(question)) ? mDNS_NewMessageID(m) :
 #endif // UNICAST_DISABLED
 #endif // UNICAST_DISABLED
-        zeroID;
-
-    debugf("mDNS_StartQuery: %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
+                          zeroID;
+    debugf("mDNS_StartQuery_internal: %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); 
+    
+    // Note: It important that new questions are appended at the *end* of the list, not prepended at the start
+    q = &m->Questions;
+    if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) 
+        q = &m->LocalOnlyQuestions;
+    while (*q && *q != question) 
+        q=&(*q)->next;
 
 
-    if (m->rrcache_size == 0)   // Can't do queries if we have no cache space allocated
-        return(mStatus_NoCache);
-    else
+    if (*q)
     {
     {
-        int i;
-        DNSQuestion **q;
-
-        if (!ValidateDomainName(&question->qname))
-        {
-            LogMsg("Attempt to start query with invalid qname %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
-            return(mStatus_Invalid);
-        }
-
-        // Note: It important that new questions are appended at the *end* of the list, not prepended at the start
-        q = &m->Questions;
-        if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) q = &m->LocalOnlyQuestions;
-        while (*q && *q != question) q=&(*q)->next;
-
-        if (*q)
-        {
-            LogMsg("Error! Tried to add a question %##s (%s) %p that's already in the active list",
-                   question->qname.c, DNSTypeName(question->qtype), question);
-            return(mStatus_AlreadyRegistered);
-        }
-
-        *q = question;
-
-        // If this question is referencing a specific interface, verify it exists
-        if (question->InterfaceID && question->InterfaceID != mDNSInterface_LocalOnly && question->InterfaceID != mDNSInterface_Unicast && question->InterfaceID != mDNSInterface_P2P)
-        {
-            NetworkInterfaceInfo *intf = FirstInterfaceForID(m, question->InterfaceID);
-            if (!intf)
-                LogMsg("Note: InterfaceID %p for question %##s (%s) not currently found in active interface list",
-                       question->InterfaceID, question->qname.c, DNSTypeName(question->qtype));
-        }
-
-        // Note: In the case where we already have the answer to this question in our cache, that may be all the client
-        // wanted, and they may immediately cancel their question. In this case, sending an actual query on the wire would
-        // be a waste. For that reason, we schedule our first query to go out in half a second (InitialQuestionInterval).
-        // If AnswerNewQuestion() finds that we have *no* relevant answers currently in our cache, then it will accelerate
-        // that to go out immediately.
-        question->next              = mDNSNULL;
-        question->qnamehash         = DomainNameHashValue(&question->qname);    // MUST do this before FindDuplicateQuestion()
-        question->DelayAnswering    = CheckForSoonToExpireRecords(m, &question->qname, question->qnamehash, HashSlot(&question->qname));
-        question->LastQTime         = m->timenow;
-        question->ThisQInterval     = InitialQuestionInterval;                  // MUST be > zero for an active question
-        question->ExpectUnicastResp = 0;
-        question->LastAnswerPktNum  = m->PktNum;
-        question->RecentAnswerPkts  = 0;
-        question->CurrentAnswers    = 0;
-        question->LargeAnswers      = 0;
-        question->UniqueAnswers     = 0;
-        question->LOAddressAnswers  = 0;
-        question->FlappingInterface1 = mDNSNULL;
-        question->FlappingInterface2 = mDNSNULL;
-        // Must do AuthInfo and SuppressQuery before calling FindDuplicateQuestion()
-        question->AuthInfo          = GetAuthInfoForQuestion(m, question);
-        if (question->SuppressUnusable)
-            question->SuppressQuery = ShouldSuppressQuery(m, &question->qname, question->qtype, question->InterfaceID);
-        else
-            question->SuppressQuery = 0;
-        question->DuplicateOf       = FindDuplicateQuestion(m, question);
-        question->NextInDQList      = mDNSNULL;
-        question->SendQNow          = mDNSNULL;
-        question->SendOnAll         = mDNSfalse;
-        question->RequestUnicast    = 0;
-        question->LastQTxTime       = m->timenow;
-        question->CNAMEReferrals    = 0;
-
-        // We'll create our question->LocalSocket on demand, if needed.
-        // We won't need one for duplicate questions, or from questions answered immediately out of the cache.
-        // We also don't need one for LLQs because (when we're using NAT) we want them all to share a single
-        // NAT mapping for receiving inbound add/remove events.
-        question->LocalSocket       = mDNSNULL;
-        question->qDNSServer        = mDNSNULL;
-        question->unansweredQueries = 0;
-        question->nta               = mDNSNULL;
-        question->servAddr          = zeroAddr;
-        question->servPort          = zeroIPPort;
-        question->tcp               = mDNSNULL;
-        question->NoAnswer          = NoAnswer_Normal;
-
-        question->state             = LLQ_InitialRequest;
-        question->ReqLease          = 0;
-        question->expire            = 0;
-        question->ntries            = 0;
-        question->id                = zeroOpaque64;
-        question->validDNSServers   = zeroOpaque64;
-        question->triedAllServersOnce = 0;
-        question->noServerResponse  = 0;
-        question->StopTime = 0;
-        if (question->WakeOnResolve)
-        {
-            question->WakeOnResolveCount = InitialWakeOnResolveCount;
-            mDNS_PurgeBeforeResolve(m, question);
-        }
-        else
-            question->WakeOnResolveCount = 0;
-
-        question->ValidationState = (question->ValidationRequired ? DNSSECValRequired : DNSSECValNotRequired);
-        question->ValidationStatus = 0;
-
+        LogMsg("mDNS_StartQuery_internal: Error! Tried to add a question %##s (%s) %p that's already in the active list",
+                question->qname.c, DNSTypeName(question->qtype), question);
+        return(mStatus_AlreadyRegistered);
+    }
+    *q = question;
+    
 
 
-        if (question->DuplicateOf) question->AuthInfo = question->DuplicateOf->AuthInfo;
+    // Intialize the question. The only ordering constraint we have today is that
+    // InitDNSSECProxyState should be called after the DNS server is selected (in
+    // InitCommonState -> InitDNSConfig) as DNS server selection affects DNSSEC
+    // validation.
 
 
-        for (i=0; i<DupSuppressInfoSize; i++)
-            question->DupSuppress[i].InterfaceID = mDNSNULL;
+    purge = InitCommonState(m, question);
+    InitWABState(question);
+    InitLLQState(question);
+    InitDNSSECProxyState(m, question);
 
 
-        debugf("mDNS_StartQuery: Question %##s (%s) Interface %p Now %d Send in %d Answer in %d (%p) %s (%p)",
-               question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, m->timenow,
-               NextQSendTime(question) - m->timenow,
-               question->DelayAnswering ? question->DelayAnswering - m->timenow : 0,
-               question, question->DuplicateOf ? "duplicate of" : "not duplicate", question->DuplicateOf);
+    // FindDuplicateQuestion should be called last after all the intialization
+    // as the duplicate logic could be potentially based on any field in the
+    // question.
+    question->DuplicateOf  = FindDuplicateQuestion(m, question);
+    if (question->DuplicateOf) 
+        question->AuthInfo = question->DuplicateOf->AuthInfo; 
 
 
-        if (question->DelayAnswering)
-            LogInfo("mDNS_StartQuery_internal: Delaying answering for %d ticks while cache stabilizes for %##s (%s)",
-                    question->DelayAnswering - m->timenow, question->qname.c, DNSTypeName(question->qtype));
+    if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P)
+    {
+        if (!m->NewLocalOnlyQuestions) 
+            m->NewLocalOnlyQuestions = question;
+    }
+    else
+    {
+        if (!m->NewQuestions) 
+            m->NewQuestions = question;
 
 
-        if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P)
+        // If the question's id is non-zero, then it's Wide Area
+        // MUST NOT do this Wide Area setup until near the end of
+        // mDNS_StartQuery_internal -- this code may itself issue queries (e.g. SOA,
+        // NS, etc.) and if we haven't finished setting up our own question and setting
+        // m->NewQuestions if necessary then we could end up recursively re-entering
+        // this routine with the question list data structures in an inconsistent state.
+        if (!mDNSOpaque16IsZero(question->TargetQID))
         {
         {
-            if (!m->NewLocalOnlyQuestions) m->NewLocalOnlyQuestions = question;
+            FinalizeUnicastQuestion(m, question, purge);
         }
         else
         {
         }
         else
         {
-            if (!m->NewQuestions) m->NewQuestions = question;
-
-            // If the question's id is non-zero, then it's Wide Area
-            // MUST NOT do this Wide Area setup until near the end of
-            // mDNS_StartQuery_internal -- this code may itself issue queries (e.g. SOA,
-            // NS, etc.) and if we haven't finished setting up our own question and setting
-            // m->NewQuestions if necessary then we could end up recursively re-entering
-            // this routine with the question list data structures in an inconsistent state.
-            if (!mDNSOpaque16IsZero(question->TargetQID))
-            {
-                // 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), 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
-                {
-                    question->qDNSServer = GetServerForQuestion(m, question);
-                    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));
-                }
-                // If we are talking to a server on the local host, unsupress the query. This happens if we have
-                // a DNS server running locally while we don't have any interfaces UP.
-                //
-                // TBD: Re-organise the code so that we can move this logic to ShouldSuppressQuery
-                if (question->SuppressQuery && question->qDNSServer && mDNSAddressIsLoopback(&question->qDNSServer->addr))
-                {
-                    LogInfo("mDNS_StartQuery_internal: question %p %##s (%s) unsuppressed due to local DNS Server %#a:%d",
-                            question, question->qname.c, DNSTypeName(question->qtype), &question->qDNSServer->addr,
-                            mDNSVal16(question->qDNSServer->port));
-                    question->SuppressQuery = 0;
-                }
-                ActivateUnicastQuery(m, question, mDNSfalse);
-
-                // If there is a negative cache entry for this question and if it does
-                // not have cached nsecs, then we can't validate possibly. Hence, flush
-                // them so that we can reissue the question again with EDNS0/DO bit set.
-                if (!question->DuplicateOf && DNSSECQuestion(question))
-                    mDNS_CheckForCachedNSECS(m, question);
-
-                // If long-lived query, and we don't have our NAT mapping active, start it now
-                if (question->LongLived && !m->LLQNAT.clientContext)
-                {
-                    m->LLQNAT.Protocol       = NATOp_MapUDP;
-                    m->LLQNAT.IntPort        = m->UnicastPort4;
-                    m->LLQNAT.RequestedPort  = m->UnicastPort4;
-                    m->LLQNAT.clientCallback = LLQNATCallback;
-                    m->LLQNAT.clientContext  = (void*)1; // Means LLQ NAT Traversal is active
-                    mDNS_StartNATOperation_internal(m, &m->LLQNAT);
-                }
-
-#if APPLE_OSX_mDNSResponder
-                if (question->LongLived)
-                    UpdateAutoTunnelDomainStatuses(m);
-#endif
-
-            }
-            else
+            if (purge)
             {
             {
-                if (question->TimeoutQuestion)
-                    question->StopTime = NonZeroTime(m->timenow + GetTimeoutForMcastQuestion(m, question) * mDNSPlatformOneSecond);
+                LogInfo("mDNS_StartQuery_internal: Purging for %##s", question->qname.c);
+                mDNS_PurgeForQuestion(m, question);
             }
             }
-            if (question->StopTime) SetNextQueryStopTime(m, question);
-            SetNextQueryTime(m,question);
         }
         }
-
-        return(mStatus_NoError);
     }
     }
+
+    return(mStatus_NoError);
 }
 
 // CancelGetZoneData is an internal routine (i.e. must be called with the lock already held)
 }
 
 // CancelGetZoneData is an internal routine (i.e. must be called with the lock already held)
@@ -9891,13 +11828,15 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que
             if (!mDNSOpaque16IsZero(q->TargetQID) && q->LongLived) break;
         if (!q)
         {
             if (!mDNSOpaque16IsZero(q->TargetQID) && q->LongLived) break;
         if (!q)
         {
-            if (!m->LLQNAT.clientContext)       // Should never happen, but just in case...
-                LogMsg("mDNS_StopQuery ERROR LLQNAT.clientContext NULL");
+            if (!m->LLQNAT.clientCallback)       // Should never happen, but just in case...
+            {
+                LogMsg("mDNS_StopQuery ERROR LLQNAT.clientCallback NULL");
+            }
             else
             {
                 LogInfo("Stopping LLQNAT");
                 mDNS_StopNATOperation_internal(m, &m->LLQNAT);
             else
             {
                 LogInfo("Stopping LLQNAT");
                 mDNS_StopNATOperation_internal(m, &m->LLQNAT);
-                m->LLQNAT.clientContext = mDNSNULL; // Means LLQ NAT Traversal not running
+                m->LLQNAT.clientCallback = mDNSNULL; // Means LLQ NAT Traversal not running
             }
         }
 
             }
         }
 
@@ -9923,6 +11862,18 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que
     // wait until we send the refresh above which needs the nta
     if (question->nta) { CancelGetZoneData(m, question->nta); question->nta = mDNSNULL; }
 
     // wait until we send the refresh above which needs the nta
     if (question->nta) { CancelGetZoneData(m, question->nta); question->nta = mDNSNULL; }
 
+    if (question->ValidationRequired && question->DNSSECAuthInfo)
+    {
+        LogInfo("mDNS_StopQuery_internal: freeing DNSSECAuthInfo %##s", question->qname.c);
+        question->DAIFreeCallback(m, question->DNSSECAuthInfo);
+        question->DNSSECAuthInfo = mDNSNULL;
+    }
+    if (question->AnonInfo)
+    {
+        FreeAnonInfo(question->AnonInfo);
+        question->AnonInfo = mDNSNULL;
+    }
+
     return(mStatus_NoError);
 }
 
     return(mStatus_NoError);
 }
 
@@ -10001,7 +11952,7 @@ mDNSexport mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr
 
 mDNSlocal mStatus mDNS_StartBrowse_internal(mDNS *const m, DNSQuestion *const question,
                                             const domainname *const srv, const domainname *const domain,
 
 mDNSlocal mStatus mDNS_StartBrowse_internal(mDNS *const m, DNSQuestion *const question,
                                             const domainname *const srv, const domainname *const domain,
-                                            const mDNSInterfaceID InterfaceID, mDNSu32 flags,
+                                            const mDNSu8 *anondata, const mDNSInterfaceID InterfaceID, mDNSu32 flags,
                                             mDNSBool ForceMCast, mDNSBool useBackgroundTrafficClass,
                                             mDNSQuestionCallback *Callback, void *Context)
 {
                                             mDNSBool ForceMCast, mDNSBool useBackgroundTrafficClass,
                                             mDNSQuestionCallback *Callback, void *Context)
 {
@@ -10020,26 +11971,37 @@ mDNSlocal mStatus mDNS_StartBrowse_internal(mDNS *const m, DNSQuestion *const qu
     question->RetryWithSearchDomains = mDNSfalse;
     question->TimeoutQuestion  = 0;
     question->WakeOnResolve    = 0;
     question->RetryWithSearchDomains = mDNSfalse;
     question->TimeoutQuestion  = 0;
     question->WakeOnResolve    = 0;
-    question->UseBrackgroundTrafficClass = useBackgroundTrafficClass;
+    question->UseBackgroundTrafficClass = useBackgroundTrafficClass;
     question->ValidationRequired = 0;
     question->ValidatingResponse = 0;
     question->ValidationRequired = 0;
     question->ValidatingResponse = 0;
+    question->ProxyQuestion    = 0;
     question->qnameOrig        = mDNSNULL;
     question->qnameOrig        = mDNSNULL;
+    question->AnonInfo         = mDNSNULL;
     question->QuestionCallback = Callback;
     question->QuestionContext  = Context;
     question->QuestionCallback = Callback;
     question->QuestionContext  = Context;
-    if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) return(mStatus_BadParamErr);
+
+    if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain))
+        return(mStatus_BadParamErr);
+
+    if (anondata)
+    {
+        question->AnonInfo = AllocateAnonInfo(&question->qname, anondata, mDNSPlatformStrLen(anondata), mDNSNULL);
+        if (!question->AnonInfo)
+            return(mStatus_BadParamErr);
+    }
 
     return(mDNS_StartQuery_internal(m, question));
 }
 
 mDNSexport mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question,
                                     const domainname *const srv, const domainname *const domain,
 
     return(mDNS_StartQuery_internal(m, question));
 }
 
 mDNSexport mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question,
                                     const domainname *const srv, const domainname *const domain,
-                                    const mDNSInterfaceID InterfaceID, mDNSu32 flags,
+                                    const mDNSu8 *anondata, const mDNSInterfaceID InterfaceID, mDNSu32 flags,
                                     mDNSBool ForceMCast, mDNSBool useBackgroundTrafficClass,
                                     mDNSQuestionCallback *Callback, void *Context)
 {
     mStatus status;
     mDNS_Lock(m);
                                     mDNSBool ForceMCast, mDNSBool useBackgroundTrafficClass,
                                     mDNSQuestionCallback *Callback, void *Context)
 {
     mStatus status;
     mDNS_Lock(m);
-    status = mDNS_StartBrowse_internal(m, question, srv, domain, InterfaceID, flags, ForceMCast, useBackgroundTrafficClass, Callback, Context);
+    status = mDNS_StartBrowse_internal(m, question, srv, domain, anondata, InterfaceID, flags, ForceMCast, useBackgroundTrafficClass, Callback, Context);
     mDNS_Unlock(m);
     return(status);
 }
     mDNS_Unlock(m);
     return(status);
 }
@@ -10205,10 +12167,12 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m,
     query->qSRV.RetryWithSearchDomains = mDNSfalse;
     query->qSRV.TimeoutQuestion     = 0;
     query->qSRV.WakeOnResolve       = 0;
     query->qSRV.RetryWithSearchDomains = mDNSfalse;
     query->qSRV.TimeoutQuestion     = 0;
     query->qSRV.WakeOnResolve       = 0;
-    query->qSRV.UseBrackgroundTrafficClass = mDNSfalse;
+    query->qSRV.UseBackgroundTrafficClass = mDNSfalse;
     query->qSRV.ValidationRequired  = 0;
     query->qSRV.ValidatingResponse  = 0;
     query->qSRV.ValidationRequired  = 0;
     query->qSRV.ValidatingResponse  = 0;
+    query->qSRV.ProxyQuestion       = 0;
     query->qSRV.qnameOrig           = mDNSNULL;
     query->qSRV.qnameOrig           = mDNSNULL;
+    query->qSRV.AnonInfo            = mDNSNULL;
     query->qSRV.QuestionCallback    = FoundServiceInfoSRV;
     query->qSRV.QuestionContext     = query;
 
     query->qSRV.QuestionCallback    = FoundServiceInfoSRV;
     query->qSRV.QuestionContext     = query;
 
@@ -10229,10 +12193,12 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m,
     query->qTXT.RetryWithSearchDomains = mDNSfalse;
     query->qTXT.TimeoutQuestion     = 0;
     query->qTXT.WakeOnResolve       = 0;
     query->qTXT.RetryWithSearchDomains = mDNSfalse;
     query->qTXT.TimeoutQuestion     = 0;
     query->qTXT.WakeOnResolve       = 0;
-    query->qTXT.UseBrackgroundTrafficClass = mDNSfalse;
+    query->qTXT.UseBackgroundTrafficClass = mDNSfalse;
     query->qTXT.ValidationRequired  = 0;
     query->qTXT.ValidatingResponse  = 0;
     query->qTXT.ValidationRequired  = 0;
     query->qTXT.ValidatingResponse  = 0;
+    query->qTXT.ProxyQuestion       = 0;
     query->qTXT.qnameOrig           = mDNSNULL;
     query->qTXT.qnameOrig           = mDNSNULL;
+    query->qTXT.AnonInfo            = mDNSNULL;
     query->qTXT.QuestionCallback    = FoundServiceInfoTXT;
     query->qTXT.QuestionContext     = query;
 
     query->qTXT.QuestionCallback    = FoundServiceInfoTXT;
     query->qTXT.QuestionContext     = query;
 
@@ -10253,10 +12219,12 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m,
     query->qAv4.RetryWithSearchDomains = mDNSfalse;
     query->qAv4.TimeoutQuestion     = 0;
     query->qAv4.WakeOnResolve       = 0;
     query->qAv4.RetryWithSearchDomains = mDNSfalse;
     query->qAv4.TimeoutQuestion     = 0;
     query->qAv4.WakeOnResolve       = 0;
-    query->qAv4.UseBrackgroundTrafficClass = mDNSfalse;
+    query->qAv4.UseBackgroundTrafficClass = mDNSfalse;
     query->qAv4.ValidationRequired  = 0;
     query->qAv4.ValidatingResponse  = 0;
     query->qAv4.ValidationRequired  = 0;
     query->qAv4.ValidatingResponse  = 0;
+    query->qAv4.ProxyQuestion       = 0;
     query->qAv4.qnameOrig           = mDNSNULL;
     query->qAv4.qnameOrig           = mDNSNULL;
+    query->qAv4.AnonInfo            = mDNSNULL;
     query->qAv4.QuestionCallback    = FoundServiceInfo;
     query->qAv4.QuestionContext     = query;
 
     query->qAv4.QuestionCallback    = FoundServiceInfo;
     query->qAv4.QuestionContext     = query;
 
@@ -10276,10 +12244,12 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m,
     query->qAv6.AppendSearchDomains = 0;
     query->qAv6.RetryWithSearchDomains = mDNSfalse;
     query->qAv6.TimeoutQuestion     = 0;
     query->qAv6.AppendSearchDomains = 0;
     query->qAv6.RetryWithSearchDomains = mDNSfalse;
     query->qAv6.TimeoutQuestion     = 0;
-    query->qAv6.UseBrackgroundTrafficClass = mDNSfalse;
+    query->qAv6.UseBackgroundTrafficClass = mDNSfalse;
     query->qAv6.ValidationRequired  = 0;
     query->qAv6.ValidatingResponse  = 0;
     query->qAv6.ValidationRequired  = 0;
     query->qAv6.ValidatingResponse  = 0;
+    query->qAv6.ProxyQuestion       = 0;
     query->qAv6.qnameOrig           = mDNSNULL;
     query->qAv6.qnameOrig           = mDNSNULL;
+    query->qAv6.AnonInfo            = mDNSNULL;
     query->qAv6.QuestionCallback    = FoundServiceInfo;
     query->qAv6.QuestionContext     = query;
 
     query->qAv6.QuestionCallback    = FoundServiceInfo;
     query->qAv6.QuestionContext     = query;
 
@@ -10336,10 +12306,13 @@ mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, m
     question->RetryWithSearchDomains = mDNSfalse;
     question->TimeoutQuestion  = 0;
     question->WakeOnResolve    = 0;
     question->RetryWithSearchDomains = mDNSfalse;
     question->TimeoutQuestion  = 0;
     question->WakeOnResolve    = 0;
-    question->UseBrackgroundTrafficClass = mDNSfalse;
+    question->UseBackgroundTrafficClass = mDNSfalse;
     question->ValidationRequired = 0;
     question->ValidatingResponse = 0;
     question->ValidationRequired = 0;
     question->ValidatingResponse = 0;
+    question->ProxyQuestion    = 0;
     question->qnameOrig        = mDNSNULL;
     question->qnameOrig        = mDNSNULL;
+    question->AnonInfo         = mDNSNULL;
+    question->pid              = mDNSPlatformGetPID();
     question->QuestionCallback = Callback;
     question->QuestionContext  = Context;
     if (DomainType > mDNS_DomainTypeMax) return(mStatus_BadParamErr);
     question->QuestionCallback = Callback;
     question->QuestionContext  = Context;
     if (DomainType > mDNS_DomainTypeMax) return(mStatus_BadParamErr);
@@ -10455,7 +12428,22 @@ mDNSlocal NetworkInterfaceInfo *FindFirstAdvertisedInterface(mDNS *const m)
 mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set)
 {
     char buffer[MAX_REVERSE_MAPPING_NAME];
 mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set)
 {
     char buffer[MAX_REVERSE_MAPPING_NAME];
-    NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m);
+    NetworkInterfaceInfo *primary;
+
+    if (!set->McastTxRx)
+    {
+        LogInfo("AdvertiseInterface: Returning, not multicast capable %s", set->ifname);
+        return;
+    }
+#if TARGET_OS_EMBEDDED
+    if (!m->AutoTargetServices)
+    {
+        LogInfo("AdvertiseInterface: Returning due to AutoTargetServices zero for %s", set->ifname);
+        return;
+    }
+#endif
+
+    primary = FindFirstAdvertisedInterface(m);
     if (!primary) primary = set; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary
 
     // Send dynamic update for non-linklocal IPv4 Addresses
     if (!primary) primary = set; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary
 
     // Send dynamic update for non-linklocal IPv4 Addresses
@@ -10501,6 +12489,10 @@ mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set)
 
     set->RR_A.RRSet = &primary->RR_A;           // May refer to self
 
 
     set->RR_A.RRSet = &primary->RR_A;           // May refer to self
 
+#if APPLE_OSX_mDNSResponder
+    D2D_start_advertising_interface(set);
+#endif // APPLE_OSX_mDNSResponder
+
     mDNS_Register_internal(m, &set->RR_A);
     mDNS_Register_internal(m, &set->RR_PTR);
 
     mDNS_Register_internal(m, &set->RR_A);
     mDNS_Register_internal(m, &set->RR_PTR);
 
@@ -10540,6 +12532,41 @@ mDNSlocal void DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set)
     if (set->RR_A.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_A,     mDNS_Dereg_normal);
     if (set->RR_PTR.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_PTR,   mDNS_Dereg_normal);
     if (set->RR_HINFO.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_HINFO, mDNS_Dereg_normal);
     if (set->RR_A.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_A,     mDNS_Dereg_normal);
     if (set->RR_PTR.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_PTR,   mDNS_Dereg_normal);
     if (set->RR_HINFO.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_HINFO, mDNS_Dereg_normal);
+
+#if APPLE_OSX_mDNSResponder
+    D2D_stop_advertising_interface(set);
+#endif // APPLE_OSX_mDNSResponder
+
+}
+
+mDNSlocal void AdvertiseAllInterfaceRecords(mDNS *const m)
+{
+    NetworkInterfaceInfo *intf;
+    for (intf = m->HostInterfaces; intf; intf = intf->next)
+    {
+        if (intf->Advertise)
+        {
+            LogInfo("AdvertiseInterface: Advertising for ifname %s", intf->ifname);
+            AdvertiseInterface(m, intf);
+        }
+    }
+}
+
+mDNSlocal void DeadvertiseAllInterfaceRecords(mDNS *const m)
+{
+#if TARGET_OS_EMBEDDED
+    NetworkInterfaceInfo *intf;
+    for (intf = m->HostInterfaces; intf; intf = intf->next)
+    {
+        if (intf->Advertise)
+        {
+            LogInfo("DeadvertiseInterface: Deadvertising for ifname %s", intf->ifname);
+            DeadvertiseInterface(m, intf);
+        }
+    }
+#else
+    (void) m; //unused
+#endif
 }
 
 mDNSexport void mDNS_SetFQDN(mDNS *const m)
 }
 
 mDNSexport void mDNS_SetFQDN(mDNS *const m)
@@ -10654,6 +12681,12 @@ mDNSlocal void RestartRecordGetZoneData(mDNS * const m)
 mDNSlocal void InitializeNetWakeState(mDNS *const m, NetworkInterfaceInfo *set)
 {
     int i;
 mDNSlocal void InitializeNetWakeState(mDNS *const m, NetworkInterfaceInfo *set)
 {
     int i;
+    // We initialize ThisQInterval to -1 indicating that the question has not been started
+    // yet. If the question (browse) is started later during interface registration, it will
+    // be stopped during interface deregistration. We can't sanity check to see if the
+    // question has been stopped or not before initializing it to -1 because we need to
+    // initialize it to -1 the very first time.
+    
     set->NetWakeBrowse.ThisQInterval = -1;
     for (i=0; i<3; i++)
     {
     set->NetWakeBrowse.ThisQInterval = -1;
     for (i=0; i<3; i++)
     {
@@ -10673,7 +12706,7 @@ mDNSexport void mDNS_ActivateNetWake_internal(mDNS *const m, NetworkInterfaceInf
     if (set->InterfaceActive)
     {
         LogSPS("ActivateNetWake for %s (%#a)", set->ifname, &set->ip);
     if (set->InterfaceActive)
     {
         LogSPS("ActivateNetWake for %s (%#a)", set->ifname, &set->ip);
-        mDNS_StartBrowse_internal(m, &set->NetWakeBrowse, &SleepProxyServiceType, &localdomain, set->InterfaceID, 0, mDNSfalse, mDNSfalse, m->SPSBrowseCallback, set);
+        mDNS_StartBrowse_internal(m, &set->NetWakeBrowse, &SleepProxyServiceType, &localdomain, mDNSNULL, set->InterfaceID, 0, mDNSfalse, mDNSfalse, m->SPSBrowseCallback, set);
     }
 }
 
     }
 }
 
@@ -10683,6 +12716,10 @@ mDNSexport void mDNS_DeactivateNetWake_internal(mDNS *const m, NetworkInterfaceI
     while (p && p != set) p=p->next;
     if (!p) { LogMsg("mDNS_DeactivateNetWake_internal: NetworkInterfaceInfo %p not found in active list", set); return; }
 
     while (p && p != set) p=p->next;
     if (!p) { LogMsg("mDNS_DeactivateNetWake_internal: NetworkInterfaceInfo %p not found in active list", set); return; }
 
+    // Note: We start the browse only if the interface is NetWake capable and we use this to
+    // stop the resolves also. Hence, the resolves should not be started without the browse
+    // being started i.e, resolves should not happen unless NetWake capable which is
+    // guaranteed by BeginSleepProcessing.
     if (set->NetWakeBrowse.ThisQInterval >= 0)
     {
         int i;
     if (set->NetWakeBrowse.ThisQInterval >= 0)
     {
         int i;
@@ -10791,7 +12828,11 @@ 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);
 
         // See <rdar://problem/4073853> mDNS: m->SuppressSending set too enthusiastically
         if (!m->SuppressSending) m->SuppressSending = m->timenow + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval);
 
-        if (flapping) LogMsg("mDNS_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);
+            m->mDNSStats.InterfaceUpFlap++;
+        }
 
         LogInfo("mDNS_RegisterInterface: %s (%#a) probedelay %d", set->ifname, &set->ip, probedelay);
         if (m->SuppressProbes == 0 ||
 
         LogInfo("mDNS_RegisterInterface: %s (%#a) probedelay %d", set->ifname, &set->ip, probedelay);
         if (m->SuppressProbes == 0 ||
@@ -10803,16 +12844,13 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s
         // 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);
         // 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);
-
-        m->ClearSPSRecords = NonZeroTime(m->timenow + 60 * mDNSPlatformOneSecond);
-
-        // Clear the flag that ignores IPv6 neighbor advertisements after 2 seconds.
-        m->clearIgnoreNA = NonZeroTime(m->timenow + 2  * mDNSPlatformOneSecond);
-
         LogInfo("mDNS_RegisterInterface: Setting AnnounceOwner");
 
         LogInfo("mDNS_RegisterInterface: Setting AnnounceOwner");
 
+        m->mDNSStats.InterfaceUp++;
         for (q = m->Questions; q; q=q->next)                                // Scan our list of questions
         for (q = m->Questions; q; q=q->next)                                // Scan our list of questions
+        {
             if (mDNSOpaque16IsZero(q->TargetQID))
             if (mDNSOpaque16IsZero(q->TargetQID))
+            {
                 if (!q->InterfaceID || q->InterfaceID == set->InterfaceID)      // If non-specific Q, or Q on this specific interface,
                 {                                                               // then reactivate this question
                     // If flapping, delay between first and second queries is nine seconds instead of one second
                 if (!q->InterfaceID || q->InterfaceID == set->InterfaceID)      // If non-specific Q, or Q on this specific interface,
                 {                                                               // then reactivate this question
                     // If flapping, delay between first and second queries is nine seconds instead of one second
@@ -10824,24 +12862,41 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s
                     if (!q->ThisQInterval || q->ThisQInterval > initial)
                     {
                         q->ThisQInterval = initial;
                     if (!q->ThisQInterval || q->ThisQInterval > initial)
                     {
                         q->ThisQInterval = initial;
-                        q->RequestUnicast = 2; // Set to 2 because is decremented once *before* we check it
+
+#if mDNS_REQUEST_UNICAST_RESPONSE
+                        q->RequestUnicast = SET_QU_IN_FIRST_FOUR_QUERIES;
+#else   // mDNS_REQUEST_UNICAST_RESPONSE
+                        q->RequestUnicast = SET_QU_IN_FIRST_QUERY;
+#endif  // mDNS_REQUEST_UNICAST_RESPONSE
+
                     }
                     q->LastQTime = m->timenow - q->ThisQInterval + qdelay;
                     q->RecentAnswerPkts = 0;
                     }
                     q->LastQTime = m->timenow - q->ThisQInterval + qdelay;
                     q->RecentAnswerPkts = 0;
+                    // Change the salt
+                    ReInitAnonInfo(&q->AnonInfo, &q->qname);
                     SetNextQueryTime(m,q);
                 }
                     SetNextQueryTime(m,q);
                 }
+            }
+        }
 
         // For all our non-specific authoritative resource records (and any dormant records specific to this interface)
         // we now need them to re-probe if necessary, and then re-announce.
         for (rr = m->ResourceRecords; rr; rr=rr->next)
 
         // For all our non-specific authoritative resource records (and any dormant records specific to this interface)
         // we now need them to re-probe if necessary, and then re-announce.
         for (rr = m->ResourceRecords; rr; rr=rr->next)
+        {
             if (!rr->resrec.InterfaceID || rr->resrec.InterfaceID == set->InterfaceID)
             if (!rr->resrec.InterfaceID || rr->resrec.InterfaceID == set->InterfaceID)
-                    mDNSCoreRestartRegistration(m, rr, numannounce);
+            {
+                // Change the salt
+                ReInitAnonInfo(&rr->resrec.AnonInfo, rr->resrec.name);
+                mDNSCoreRestartRegistration(m, rr, numannounce);
+            }
+        }
+#if APPLE_OSX_mDNSResponder && !TARGET_OS_IPHONE
+        DNSSECProbe(m);
+#endif
     }
 
     RestartRecordGetZoneData(m);
 
     }
 
     RestartRecordGetZoneData(m);
 
-    CheckSuppressUnusableQuestions(m);
-
     mDNS_UpdateAllowSleep(m);
 
     mDNS_Unlock(m);
     mDNS_UpdateAllowSleep(m);
 
     mDNS_Unlock(m);
@@ -10903,13 +12958,17 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se
             CacheGroup *cg;
             CacheRecord *rr;
             DNSQuestion *q;
             CacheGroup *cg;
             CacheRecord *rr;
             DNSQuestion *q;
-            DNSServer *s;
 
             LogInfo("mDNS_DeregisterInterface: Last representative of InterfaceID %p %s (%#a) deregistered;"
                     " marking questions etc. dormant", set->InterfaceID, set->ifname, &set->ip);
 
 
             LogInfo("mDNS_DeregisterInterface: Last representative of InterfaceID %p %s (%#a) deregistered;"
                     " marking questions etc. dormant", set->InterfaceID, set->ifname, &set->ip);
 
+            m->mDNSStats.InterfaceDown++;
+
             if (set->McastTxRx && flapping)
             if (set->McastTxRx && flapping)
+            {
                 LogMsg("DeregisterInterface: Frequent transitions for interface %s (%#a)", set->ifname, &set->ip);
                 LogMsg("DeregisterInterface: Frequent transitions for interface %s (%#a)", set->ifname, &set->ip);
+                m->mDNSStats.InterfaceDownFlap++;
+            }
 
             // 1. Deactivate any questions specific to this interface, and tag appropriate questions
             // so that mDNS_RegisterInterface() knows how swiftly it needs to reactivate them
 
             // 1. Deactivate any questions specific to this interface, and tag appropriate questions
             // so that mDNS_RegisterInterface() knows how swiftly it needs to reactivate them
@@ -10945,14 +13004,6 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se
                     }
                 }
             }
                     }
                 }
             }
-
-            // 3. Any DNS servers specific to this interface are now unusable
-            for (s = m->DNSServers; s; s = s->next)
-                if (s->interface == set->InterfaceID)
-                {
-                    s->interface = mDNSInterface_Any;
-                    s->teststate = DNSServer_Disabled;
-                }
         }
     }
 
         }
     }
 
@@ -10973,13 +13024,57 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se
             mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForFlappingInterface);
     }
 
             mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForFlappingInterface);
     }
 
-    CheckSuppressUnusableQuestions(m);
-
     mDNS_UpdateAllowSleep(m);
 
     mDNS_Unlock(m);
 }
 
     mDNS_UpdateAllowSleep(m);
 
     mDNS_Unlock(m);
 }
 
+mDNSlocal void SetAnonInfoSRS(ServiceRecordSet *sr, int NumSubTypes)
+{
+    int i, len;
+
+    if (!sr->AnonData)
+        return;
+
+    len = mDNSPlatformStrLen(sr->AnonData);
+    if (sr->RR_PTR.resrec.AnonInfo)
+    {
+        LogMsg("SetAnonInfoSRS: Freeing AnonInfo for PTR record %##s, should have been freed already", sr->RR_PTR.resrec.name->c);
+        FreeAnonInfo(sr->RR_PTR.resrec.AnonInfo);
+    }
+    sr->RR_PTR.resrec.AnonInfo = AllocateAnonInfo(sr->RR_PTR.resrec.name, sr->AnonData, len, mDNSNULL);
+    for (i=0; i<NumSubTypes; i++)
+    {
+        if (sr->SubTypes[i].resrec.AnonInfo)
+        {
+            LogMsg("SetAnonInfoSRS: Freeing AnonInfo for subtype record %##s, should have been freed already", sr->SubTypes[i].resrec.name->c);
+            FreeAnonInfo(sr->SubTypes[i].resrec.AnonInfo);
+        }
+        sr->SubTypes[i].resrec.AnonInfo = AllocateAnonInfo(sr->SubTypes[i].resrec.name, sr->AnonData, len, mDNSNULL);
+    }
+}
+
+mDNSlocal void ResetAnonInfoSRS(ServiceRecordSet *sr, int NumSubTypes)
+{
+    int i;
+
+    if (!sr->AnonData)
+        return;
+    if (sr->RR_PTR.resrec.AnonInfo)
+    {
+        FreeAnonInfo(sr->RR_PTR.resrec.AnonInfo);
+        sr->RR_PTR.resrec.AnonInfo = mDNSNULL;
+    }
+    for (i=0; i<NumSubTypes; i++)
+    {
+        if (sr->SubTypes[i].resrec.AnonInfo)
+        {
+            FreeAnonInfo(sr->SubTypes[i].resrec.AnonInfo);
+            sr->SubTypes[i].resrec.AnonInfo = mDNSNULL;
+        }
+    }
+}
+
 mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
 {
     ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext;
 mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
 {
     ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext;
@@ -11025,6 +13120,7 @@ mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus resu
             if (e->r.resrec.RecordType != kDNSRecordTypeUnregistered) return;
             e = e->next;
         }
             if (e->r.resrec.RecordType != kDNSRecordTypeUnregistered) return;
             e = e->next;
         }
+        ResetAnonInfoSRS(sr, sr->NumSubTypes);
 
         // If this ServiceRecordSet was forcibly deregistered, and now its memory is ready for reuse,
         // then we can now report the NameConflict to the client
 
         // If this ServiceRecordSet was forcibly deregistered, and now its memory is ready for reuse,
         // then we can now report the NameConflict to the client
@@ -11055,6 +13151,9 @@ mDNSlocal AuthRecType setAuthRecType(mDNSInterfaceID InterfaceID, mDNSu32 flags)
         artype = AuthRecordLocalOnly;
     else if (InterfaceID == mDNSInterface_P2P)
         artype = AuthRecordP2P;
         artype = AuthRecordLocalOnly;
     else if (InterfaceID == mDNSInterface_P2P)
         artype = AuthRecordP2P;
+    else if ((InterfaceID == mDNSInterface_Any) && (flags & coreFlagIncludeP2P)
+            && (flags & coreFlagIncludeAWDL))
+        artype = AuthRecordAnyIncludeAWDLandP2P;
     else if ((InterfaceID == mDNSInterface_Any) && (flags & coreFlagIncludeP2P))
         artype = AuthRecordAnyIncludeP2P;
     else if ((InterfaceID == mDNSInterface_Any) && (flags & coreFlagIncludeAWDL))
     else if ((InterfaceID == mDNSInterface_Any) && (flags & coreFlagIncludeP2P))
         artype = AuthRecordAnyIncludeP2P;
     else if ((InterfaceID == mDNSInterface_Any) && (flags & coreFlagIncludeAWDL))
@@ -11093,6 +13192,7 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr,
     sr->Extras          = mDNSNULL;
     sr->NumSubTypes     = NumSubTypes;
     sr->SubTypes        = SubTypes;
     sr->Extras          = mDNSNULL;
     sr->NumSubTypes     = NumSubTypes;
     sr->SubTypes        = SubTypes;
+    sr->flags           = flags;
 
     artype = setAuthRecType(InterfaceID, flags);
 
 
     artype = setAuthRecType(InterfaceID, flags);
 
@@ -11101,6 +13201,11 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *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);
 
     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 (flags & coreFlagWakeOnly)
+    {
+        sr->RR_PTR.AuthFlags = AuthFlagsWakeOnly;
+    }
+
     if (SameDomainName(type, (const domainname *) "\x4" "_ubd" "\x4" "_tcp"))
         hostTTL = kHostNameSmallTTL;
     else
     if (SameDomainName(type, (const domainname *) "\x4" "_ubd" "\x4" "_tcp"))
         hostTTL = kHostNameSmallTTL;
     else
@@ -11152,6 +13257,8 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr,
         sr->SubTypes[i].Additional1 = &sr->RR_SRV;
         sr->SubTypes[i].Additional2 = &sr->RR_TXT;
     }
         sr->SubTypes[i].Additional1 = &sr->RR_SRV;
         sr->SubTypes[i].Additional2 = &sr->RR_TXT;
     }
+    
+    SetAnonInfoSRS(sr, NumSubTypes);
 
     // 3. Set up the SRV record rdata.
     sr->RR_SRV.resrec.rdata->u.srv.priority = 0;
 
     // 3. Set up the SRV record rdata.
     sr->RR_SRV.resrec.rdata->u.srv.priority = 0;
@@ -11287,7 +13394,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,
     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, 0);
+                               sr->RR_PTR.resrec.InterfaceID, sr->ServiceCallback, sr->ServiceContext, sr->flags);
 
     // mDNS_RegisterService() just reset sr->Extras to NULL.
     // Fortunately we already grabbed ourselves a copy of this pointer (above), so we can now run
 
     // mDNS_RegisterService() just reset sr->Extras to NULL.
     // Fortunately we already grabbed ourselves a copy of this pointer (above), so we can now run
@@ -11456,6 +13563,7 @@ mDNSlocal void RestartARPProbing(mDNS *const m, AuthRecord *const rr)
 
     rr->resrec.RecordType = kDNSRecordTypeUnique;
     rr->ProbeCount        = DefaultProbeCountForTypeUnique;
 
     rr->resrec.RecordType = kDNSRecordTypeUnique;
     rr->ProbeCount        = DefaultProbeCountForTypeUnique;
+    rr->ProbeRestartCount++;
 
     // If we haven't started announcing yet (and we're not already in ten-second-delay mode) the machine is probably
     // still going to sleep, so we just reset rr->ProbeCount so we'll continue probing until it stops responding.
 
     // If we haven't started announcing yet (and we're not already in ten-second-delay mode) the machine is probably
     // still going to sleep, so we just reset rr->ProbeCount so we'll continue probing until it stops responding.
@@ -11506,9 +13614,21 @@ mDNSlocal void mDNSCoreReceiveRawARP(mDNS *const m, const ARP_EthIP *const arp,
                                         mDNSSameEthAddress(&arp->sha, &intf->MAC)       ? msg3 : msg4;
                 LogSPS("%-7s %s %.6a %.4a for %.4a -- H-MAC %.6a I-MAC %.6a %s",
                        intf->ifname, msg, &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr));
                                         mDNSSameEthAddress(&arp->sha, &intf->MAC)       ? msg3 : msg4;
                 LogSPS("%-7s %s %.6a %.4a for %.4a -- H-MAC %.6a I-MAC %.6a %s",
                        intf->ifname, msg, &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr));
-                if      (msg == msg1) RestartARPProbing(m, rr);
-                else if (msg == msg3) mDNSPlatformSetLocalAddressCacheEntry(m, &rr->AddressProxy, &rr->WakeUp.IMAC, InterfaceID);
-                else if (msg == msg4) SendARP(m, 2, rr, &arp->tpa, &arp->sha, &arp->spa, &arp->sha);
+                if (msg == msg1)
+                {
+                    if ( rr->ProbeRestartCount < MAX_PROBE_RESTARTS)
+                        RestartARPProbing(m, rr);
+                    else
+                        LogSPS("Reached maximum number of restarts for probing - %s", ARDisplayString(m,rr));
+                }
+                else if (msg == msg3)
+                {
+                    mDNSPlatformSetLocalAddressCacheEntry(m, &rr->AddressProxy, &rr->WakeUp.IMAC, InterfaceID);
+                }
+                else if (msg == msg4)
+                {
+                    SendARP(m, 2, rr, &arp->tpa, &arp->sha, &arp->spa, &arp->sha);
+                }
             }
     }
 
             }
     }
 
@@ -11527,18 +13647,28 @@ mDNSlocal void mDNSCoreReceiveRawARP(mDNS *const m, const ARP_EthIP *const arp,
         if (!mDNSSameIPv4Address(arp->spa, zerov4Addr))
             for (rr = m->ResourceRecords; rr; rr=rr->next)
                 if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering &&
         if (!mDNSSameIPv4Address(arp->spa, zerov4Addr))
             for (rr = m->ResourceRecords; rr; rr=rr->next)
                 if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering &&
-                    rr->AddressProxy.type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->AddressProxy.ip.v4, arp->spa))
+                    rr->AddressProxy.type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->AddressProxy.ip.v4, arp->spa) && (rr->ProbeRestartCount < MAX_PROBE_RESTARTS))
                 {
                 {
-                    RestartARPProbing(m, rr);
-                    if (mDNSSameEthAddress(&arp->sha, &rr->WakeUp.IMAC))
-                        LogSPS("%-7s ARP %s from owner %.6a %.4a for %-15.4a -- re-starting probing for %s", intf->ifname,
-                               mDNSSameIPv4Address(arp->spa, arp->tpa) ? "Announcement " : mDNSSameOpaque16(arp->op, ARP_op_request) ? "Request      " : "Response     ",
-                               &arp->sha, &arp->spa, &arp->tpa, ARDisplayString(m, rr));
+                    if (mDNSSameEthAddress(&zeroEthAddr, &rr->WakeUp.HMAC))
+                    {
+                        LogSPS("%-7s ARP from %.6a %.4a for %.4a -- Invalid H-MAC %.6a I-MAC %.6a %s", intf->ifname,
+                                &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr));
+                    }
                     else
                     {
                     else
                     {
-                        LogMsg("%-7s Conflicting ARP from %.6a %.4a for %.4a -- waking H-MAC %.6a I-MAC %.6a %s", intf->ifname,
-                               &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr));
-                        ScheduleWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.HMAC);
+                        RestartARPProbing(m, rr);
+                        if (mDNSSameEthAddress(&arp->sha, &rr->WakeUp.IMAC))
+                        {
+                            LogSPS("%-7s ARP %s from owner %.6a %.4a for %-15.4a -- re-starting probing for %s", intf->ifname,
+                                    mDNSSameIPv4Address(arp->spa, arp->tpa) ? "Announcement " : mDNSSameOpaque16(arp->op, ARP_op_request) ? "Request      " : "Response     ",
+                                    &arp->sha, &arp->spa, &arp->tpa, ARDisplayString(m, rr));
+                        }
+                        else
+                        {
+                            LogMsg("%-7s Conflicting ARP from %.6a %.4a for %.4a -- waking H-MAC %.6a I-MAC %.6a %s", intf->ifname,
+                                    &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr));
+                            ScheduleWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.HMAC);
+                        }
                     }
                 }
     }
                     }
                 }
     }
@@ -11591,14 +13721,19 @@ mDNSlocal void mDNSCoreReceiveRawND(mDNS *const m, const mDNSEthAddr *const sha,
                                         spa && mDNSIPv6AddressIsZero(*spa)               ? msg4 : msg5;
                 LogSPS("%-7s %s %.6a %.16a for %.16a -- H-MAC %.6a I-MAC %.6a %s",
                        intf->ifname, msg, sha, spa, &ndp->target, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr));
                                         spa && mDNSIPv6AddressIsZero(*spa)               ? msg4 : msg5;
                 LogSPS("%-7s %s %.6a %.16a for %.16a -- H-MAC %.6a I-MAC %.6a %s",
                        intf->ifname, msg, sha, spa, &ndp->target, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr));
-                if      (msg == msg1) RestartARPProbing(m, rr);
-                else if (msg == msg3)
+                if (msg == msg1)
                 {
                 {
-                    if (!(m->KnownBugs & mDNS_KnownBug_LimitedIPv6))
-                        mDNSPlatformSetLocalAddressCacheEntry(m, &rr->AddressProxy, &rr->WakeUp.IMAC, InterfaceID);
+                    if (rr->ProbeRestartCount < MAX_PROBE_RESTARTS)
+                        RestartARPProbing(m, rr);
+                    else
+                        LogSPS("Reached maximum number of restarts for probing - %s", ARDisplayString(m,rr));
                 }
                 }
-                else if (msg == msg4) SendNDP(m, NDP_Adv, NDP_Solicited, rr, &ndp->target, mDNSNULL, spa,          sha             );
-                else if (msg == msg5) SendNDP(m, NDP_Adv, 0,             rr, &ndp->target, mDNSNULL, &AllHosts_v6, &AllHosts_v6_Eth);
+                else if (msg == msg3)
+                    mDNSPlatformSetLocalAddressCacheEntry(m, &rr->AddressProxy, &rr->WakeUp.IMAC, InterfaceID);
+                else if (msg == msg4) 
+                    SendNDP(m, NDP_Adv, NDP_Solicited, rr, &ndp->target, mDNSNULL, spa, sha);
+                else if (msg == msg5) 
+                    SendNDP(m, NDP_Adv, 0, rr, &ndp->target, mDNSNULL, &AllHosts_v6, &AllHosts_v6_Eth);
             }
     }
 
             }
     }
 
@@ -11615,17 +13750,27 @@ mDNSlocal void mDNSCoreReceiveRawND(mDNS *const m, const mDNSEthAddr *const sha,
         if (!mDNSSameIPv6Address(*spa, zerov6Addr))
             for (rr = m->ResourceRecords; rr; rr=rr->next)
                 if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering &&
         if (!mDNSSameIPv6Address(*spa, zerov6Addr))
             for (rr = m->ResourceRecords; rr; rr=rr->next)
                 if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering &&
-                    rr->AddressProxy.type == mDNSAddrType_IPv6 && mDNSSameIPv6Address(rr->AddressProxy.ip.v6, *spa))
+                    rr->AddressProxy.type == mDNSAddrType_IPv6 && mDNSSameIPv6Address(rr->AddressProxy.ip.v6, *spa) && (rr->ProbeRestartCount < MAX_PROBE_RESTARTS))
                 {
                 {
-                    RestartARPProbing(m, rr);
-                    if (mDNSSameEthAddress(sha, &rr->WakeUp.IMAC))
-                        LogSPS("%-7s NDP %s from owner %.6a %.16a for %.16a -- re-starting probing for %s", intf->ifname,
-                               ndp->type == NDP_Sol ? "Solicitation " : "Advertisement", sha, spa, &ndp->target, ARDisplayString(m, rr));
+                    if (mDNSSameEthAddress(&zeroEthAddr, &rr->WakeUp.HMAC))
+                    {
+                        LogSPS("%-7s NDP from %.6a %.16a for %.16a -- Invalid H-MAC %.6a I-MAC %.6a %s", intf->ifname,
+                                    sha, spa, &ndp->target, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr));
+                    }
                     else
                     {
                     else
                     {
-                        LogMsg("%-7s Conflicting NDP from %.6a %.16a for %.16a -- waking H-MAC %.6a I-MAC %.6a %s", intf->ifname,
-                               sha, spa, &ndp->target, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr));
-                        ScheduleWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.HMAC);
+                        RestartARPProbing(m, rr);
+                        if (mDNSSameEthAddress(sha, &rr->WakeUp.IMAC))
+                        {
+                            LogSPS("%-7s NDP %s from owner %.6a %.16a for %.16a -- re-starting probing for %s", intf->ifname,
+                                    ndp->type == NDP_Sol ? "Solicitation " : "Advertisement", sha, spa, &ndp->target, ARDisplayString(m, rr));
+                        }
+                        else
+                        {
+                            LogMsg("%-7s Conflicting NDP from %.6a %.16a for %.16a -- waking H-MAC %.6a I-MAC %.6a %s", intf->ifname,
+                                    sha, spa, &ndp->target, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr));
+                            ScheduleWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.HMAC);
+                        }
                     }
                 }
     }
                     }
                 }
     }
@@ -11652,6 +13797,7 @@ mDNSlocal void mDNSCoreReceiveRawTransportPacket(mDNS *const m, const mDNSEthAdd
                     #define TH_FIN  0x01
                     #define TH_SYN  0x02
                     #define TH_RST  0x04
                     #define TH_FIN  0x01
                     #define TH_SYN  0x02
                     #define TH_RST  0x04
+                    #define TH_ACK  0x10
 
         kr = mDNS_MatchKeepaliveInfo(m, dst, src, port, t->tcp.src, &seq, &ack);
         if (kr)
 
         kr = mDNS_MatchKeepaliveInfo(m, dst, src, port, t->tcp.src, &seq, &ack);
         if (kr)
@@ -11692,6 +13838,14 @@ mDNSlocal void mDNSCoreReceiveRawTransportPacket(mDNS *const m, const mDNSEthAdd
                 tcphlen = ((t->tcp.offset >> 4) * 4);
                 if (end - ((mDNSu8 *)t + tcphlen) > 0) data = mDNStrue;
                 wake = ((int)(pack - seq) > 0) && ((int)(pseq - ack) >= 0) && data;
                 tcphlen = ((t->tcp.offset >> 4) * 4);
                 if (end - ((mDNSu8 *)t + tcphlen) > 0) data = mDNStrue;
                 wake = ((int)(pack - seq) > 0) && ((int)(pseq - ack) >= 0) && data;
+
+                // If we got a regular keepalive on a connection that was registed with the KeepAlive API, respond with an ACK
+                if ((t->tcp.flags & TH_ACK) && (data == mDNSfalse) &&
+                    ((int)(ack - pseq) == 1))
+                {
+                    // Send an ACK;
+                    mDNS_SendKeepaliveACK(m, kr);
+                }
                 LogSPS("mDNSCoreReceiveRawTransportPacket: End %p, hlen %d, Datalen %d, pack %u, seq %u, pseq %u, ack %u, wake %d",
                        end, tcphlen, end - ((mDNSu8 *)t + tcphlen), pack, seq, pseq, ack, wake);
             }
                 LogSPS("mDNSCoreReceiveRawTransportPacket: End %p, hlen %d, Datalen %d, pack %u, seq %u, pseq %u, ack %u, wake %d",
                        end, tcphlen, end - ((mDNSu8 *)t + tcphlen), pack, seq, pseq, ack, wake);
             }
@@ -11700,7 +13854,6 @@ mDNSlocal void mDNSCoreReceiveRawTransportPacket(mDNS *const m, const mDNSEthAdd
         }
         else
         {
         }
         else
         {
-
             // Plan to wake if
             // (a) RST is not set, AND
             // (b) packet is SYN, SYN+FIN, or plain data packet (no SYN or FIN). We won't wake for FIN alone.
             // Plan to wake if
             // (a) RST is not set, AND
             // (b) packet is SYN, SYN+FIN, or plain data packet (no SYN or FIN). We won't wake for FIN alone.
@@ -11856,6 +14009,7 @@ mDNSlocal void ConstructSleepProxyServerName(mDNS *const m, domainlabel *name)
                                        m->SPSType, m->SPSPortability, m->SPSMarginalPower, m->SPSTotalPower, m->SPSFeatureFlags, &m->nicelabel);
 }
 
                                        m->SPSType, m->SPSPortability, m->SPSMarginalPower, m->SPSTotalPower, m->SPSFeatureFlags, &m->nicelabel);
 }
 
+#ifndef SPC_DISABLED
 mDNSlocal void SleepProxyServerCallback(mDNS *const m, ServiceRecordSet *const srs, mStatus result)
 {
     if (result == mStatus_NameConflict)
 mDNSlocal void SleepProxyServerCallback(mDNS *const m, ServiceRecordSet *const srs, mStatus result)
 {
     if (result == mStatus_NameConflict)
@@ -11883,6 +14037,7 @@ mDNSlocal void SleepProxyServerCallback(mDNS *const m, ServiceRecordSet *const s
         }
     }
 }
         }
     }
 }
+#endif
 
 // Called with lock held
 mDNSexport void mDNSCoreBeSleepProxyServer_internal(mDNS *const m, mDNSu8 sps, mDNSu8 port, mDNSu8 marginalpower, mDNSu8 totpower, mDNSu8 features)
 
 // Called with lock held
 mDNSexport void mDNSCoreBeSleepProxyServer_internal(mDNS *const m, mDNSu8 sps, mDNSu8 port, mDNSu8 marginalpower, mDNSu8 totpower, mDNSu8 features)
@@ -11895,8 +14050,10 @@ mDNSexport void mDNSCoreBeSleepProxyServer_internal(mDNS *const m, mDNSu8 sps, m
     if (!sps && m->SPSSocket) { mDNSPlatformUDPClose(m->SPSSocket); m->SPSSocket = mDNSNULL; }
 
     // If turning off, or changing type, deregister old name
     if (!sps && m->SPSSocket) { mDNSPlatformUDPClose(m->SPSSocket); m->SPSSocket = mDNSNULL; }
 
     // If turning off, or changing type, deregister old name
+#ifndef SPC_DISABLED
     if (m->SPSState == 1 && sps != m->SPSType)
     { m->SPSState = 2; mDNS_DeregisterService_drt(m, &m->SPSRecords, sps ? mDNS_Dereg_rapid : mDNS_Dereg_normal); }
     if (m->SPSState == 1 && sps != m->SPSType)
     { m->SPSState = 2; mDNS_DeregisterService_drt(m, &m->SPSRecords, sps ? mDNS_Dereg_rapid : mDNS_Dereg_normal); }
+#endif // SPC_DISABLED
 
     // Record our new SPS parameters
     m->SPSType          = sps;
 
     // Record our new SPS parameters
     m->SPSType          = sps;
@@ -11912,7 +14069,9 @@ mDNSexport void mDNSCoreBeSleepProxyServer_internal(mDNS *const m, mDNSu8 sps, m
             m->SPSSocket = mDNSPlatformUDPSocket(m, zeroIPPort);
             if (!m->SPSSocket) { LogMsg("mDNSCoreBeSleepProxyServer: Failed to allocate SPSSocket"); goto fail; }
         }
             m->SPSSocket = mDNSPlatformUDPSocket(m, zeroIPPort);
             if (!m->SPSSocket) { LogMsg("mDNSCoreBeSleepProxyServer: Failed to allocate SPSSocket"); goto fail; }
         }
+#ifndef SPC_DISABLED
         if (m->SPSState == 0) SleepProxyServerCallback(m, &m->SPSRecords, mStatus_MemFree);
         if (m->SPSState == 0) SleepProxyServerCallback(m, &m->SPSRecords, mStatus_MemFree);
+#endif // SPC_DISABLED
     }
     else if (m->SPSState)
     {
     }
     else if (m->SPSState)
     {
@@ -11960,7 +14119,6 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p,
     if (!rrcachestorage) rrcachesize = 0;
 
     m->p                             = p;
     if (!rrcachestorage) rrcachesize = 0;
 
     m->p                             = p;
-    m->KnownBugs                     = 0;
     m->CanReceiveUnicastOn5353       = mDNSfalse; // Assume we can't receive unicasts on 5353, unless platform layer tells us otherwise
     m->AdvertiseLocalAddresses       = AdvertiseLocalAddresses;
     m->DivertMulticastAdvertisements = mDNSfalse;
     m->CanReceiveUnicastOn5353       = mDNSfalse; // Assume we can't receive unicasts on 5353, unless platform layer tells us otherwise
     m->AdvertiseLocalAddresses       = AdvertiseLocalAddresses;
     m->DivertMulticastAdvertisements = mDNSfalse;
@@ -11971,6 +14129,7 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p,
     m->MainCallback                  = Callback;
     m->MainContext                   = Context;
     m->rec.r.resrec.RecordType       = 0;
     m->MainCallback                  = Callback;
     m->MainContext                   = Context;
     m->rec.r.resrec.RecordType       = 0;
+    m->rec.r.resrec.AnonInfo         = mDNSNULL;
 
     // For debugging: To catch and report locking failures
     m->mDNS_busy               = 0;
 
     // For debugging: To catch and report locking failures
     m->mDNS_busy               = 0;
@@ -12001,16 +14160,24 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p,
     m->RandomQueryDelay        = 0;
     m->RandomReconfirmDelay    = 0;
     m->PktNum                  = 0;
     m->RandomQueryDelay        = 0;
     m->RandomReconfirmDelay    = 0;
     m->PktNum                  = 0;
+    m->MPktNum                 = 0;
     m->LocalRemoveEvents       = mDNSfalse;
     m->SleepState              = SleepState_Awake;
     m->SleepSeqNum             = 0;
     m->SystemWakeOnLANEnabled  = mDNSfalse;
     m->AnnounceOwner           = NonZeroTime(timenow + 60 * mDNSPlatformOneSecond);
     m->LocalRemoveEvents       = mDNSfalse;
     m->SleepState              = SleepState_Awake;
     m->SleepSeqNum             = 0;
     m->SystemWakeOnLANEnabled  = mDNSfalse;
     m->AnnounceOwner           = NonZeroTime(timenow + 60 * mDNSPlatformOneSecond);
-    m->ClearSPSRecords         = 0;
-    m->clearIgnoreNA           = NonZeroTime(timenow +  2 * mDNSPlatformOneSecond);
     m->DelaySleep              = 0;
     m->SleepLimit              = 0;
 
     m->DelaySleep              = 0;
     m->SleepLimit              = 0;
 
+#if APPLE_OSX_mDNSResponder
+    m->StatStartTime           = mDNSPlatformUTC();
+    m->NextStatLogTime         = m->StatStartTime + kDefaultNextStatsticsLogTime;
+    m->ActiveStatTime          = 0;
+    m->UnicastPacketsSent      = 0;
+    m->MulticastPacketsSent    = 0;
+    m->RemoteSubnet            = 0;
+#endif // APPLE_OSX_mDNSResponder
+
     // These fields only required for mDNS Searcher...
     m->Questions               = mDNSNULL;
     m->NewQuestions            = mDNSNULL;
     // These fields only required for mDNS Searcher...
     m->Questions               = mDNSNULL;
     m->NewQuestions            = mDNSNULL;
@@ -12071,15 +14238,27 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p,
     m->Hostnames                = mDNSNULL;
     m->AutoTunnelNAT.clientContext = mDNSNULL;
 
     m->Hostnames                = mDNSNULL;
     m->AutoTunnelNAT.clientContext = mDNSNULL;
 
-    m->StartWABQueries          = mDNSfalse;
-    m->mDNSHandlePeerEvents     = mDNSfalse;
+    m->WABBrowseQueriesCount    = 0;
+    m->WABLBrowseQueriesCount   = 0;
+    m->WABRegQueriesCount       = 0;
+#if !TARGET_OS_EMBEDDED
+    m->mDNSOppCaching           = mDNStrue;
+#else
+    m->mDNSOppCaching           = mDNSfalse;
+#endif
+    m->AutoTargetServices       = 0;
 
     // NAT traversal fields
 
     // NAT traversal fields
+    m->LLQNAT.clientCallback    = mDNSNULL;
+    m->LLQNAT.clientContext     = mDNSNULL;
     m->NATTraversals            = mDNSNULL;
     m->CurrentNATTraversal      = mDNSNULL;
     m->retryIntervalGetAddr     = 0;    // delta between time sent and retry
     m->retryGetAddr             = timenow + 0x78000000; // absolute time when we retry
     m->NATTraversals            = mDNSNULL;
     m->CurrentNATTraversal      = mDNSNULL;
     m->retryIntervalGetAddr     = 0;    // delta between time sent and retry
     m->retryGetAddr             = timenow + 0x78000000; // absolute time when we retry
-    m->ExternalAddress          = zerov4Addr;
+    m->ExtAddress               = zerov4Addr;
+    m->PCPNonce[0]              = mDNSRandom(-1);
+    m->PCPNonce[1]              = mDNSRandom(-1);
+    m->PCPNonce[2]              = mDNSRandom(-1);
 
     m->NATMcastRecvskt          = mDNSNULL;
     m->LastNATupseconds         = 0;
 
     m->NATMcastRecvskt          = mDNSNULL;
     m->LastNATupseconds         = 0;
@@ -12138,8 +14317,10 @@ mDNSexport void mDNS_ConfigChanged(mDNS *const m)
     if (m->SPSState == 1)
     {
         domainlabel name, newname;
     if (m->SPSState == 1)
     {
         domainlabel name, newname;
+#ifndef SPC_DISABLED
         domainname type, domain;
         DeconstructServiceName(m->SPSRecords.RR_SRV.resrec.name, &name, &type, &domain);
         domainname type, domain;
         DeconstructServiceName(m->SPSRecords.RR_SRV.resrec.name, &name, &type, &domain);
+#endif // SPC_DISABLED
         ConstructSleepProxyServerName(m, &newname);
         if (!SameDomainLabelCS(name.c, newname.c))
         {
         ConstructSleepProxyServerName(m, &newname);
         if (!SameDomainLabelCS(name.c, newname.c))
         {
@@ -12147,7 +14328,9 @@ mDNSexport void mDNS_ConfigChanged(mDNS *const m)
             // When SleepProxyServerCallback gets the mStatus_MemFree message,
             // it will reregister the service under the new name
             m->SPSState = 2;
             // When SleepProxyServerCallback gets the mStatus_MemFree message,
             // it will reregister the service under the new name
             m->SPSState = 2;
+#ifndef SPC_DISABLED
             mDNS_DeregisterService_drt(m, &m->SPSRecords, mDNS_Dereg_rapid);
             mDNS_DeregisterService_drt(m, &m->SPSRecords, mDNS_Dereg_rapid);
+#endif // SPC_DISABLED
         }
     }
 
         }
     }
 
@@ -12188,26 +14371,43 @@ mDNSlocal void PurgeOrReconfirmCacheRecord(mDNS *const m, CacheRecord *cr, const
     }
 }
 
     }
 }
 
-mDNSlocal void mDNS_PurgeBeforeResolve(mDNS *const m, DNSQuestion *q)
+mDNSlocal void mDNS_PurgeForQuestion(mDNS *const m, DNSQuestion *q)
 {
     const mDNSu32 slot = HashSlot(&q->qname);
     CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
     CacheRecord *rp;
 {
     const mDNSu32 slot = HashSlot(&q->qname);
     CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
     CacheRecord *rp;
+    mDNSu8 validatingResponse = 0;
 
 
+    // For DNSSEC questions, purge the corresponding RRSIGs also.
+    if (DNSSECQuestion(q))
+    {
+        validatingResponse = q->ValidatingResponse;
+        q->ValidatingResponse = mDNStrue;
+    }
     for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next)
     {
         if (SameNameRecordAnswersQuestion(&rp->resrec, q))
         {
     for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next)
     {
         if (SameNameRecordAnswersQuestion(&rp->resrec, q))
         {
-            LogInfo("mDNS_PurgeBeforeResolve: Flushing %s", CRDisplayString(m, rp));
+            LogInfo("mDNS_PurgeForQuestion: Flushing %s", CRDisplayString(m, rp));
             mDNS_PurgeCacheResourceRecord(m, rp);
         }
     }
             mDNS_PurgeCacheResourceRecord(m, rp);
         }
     }
+    if (DNSSECQuestion(q))
+    {
+        q->ValidatingResponse = validatingResponse;
+    }
 }
 
 }
 
-// If we need to validate the negative response, we need the NSECs to prove
-// the non-existence. If we don't have the cached NSECs, purge them so that
-// we can reissue the question with EDNS0/DO bit set.
-mDNSlocal void mDNS_CheckForCachedNSECS(mDNS *const m, DNSQuestion *q)
+// For DNSSEC question, we need the DNSSEC records also. If the cache does not
+// have the DNSSEC records, we need to re-issue the question with EDNS0/DO bit set.
+// Just re-issuing the question for RRSIGs does not work in practice as the response
+// may not contain the RRSIGs whose typeCovered field matches the question's qtype.
+//
+// For negative responses, we need the NSECs to prove the non-existence. If we don't
+// have the cached NSECs, purge them. For positive responses, if we don't have the
+// RRSIGs and if we have not already issued the question with EDNS0/DO bit set, purge
+// them.
+mDNSlocal void CheckForDNSSECRecords(mDNS *const m, DNSQuestion *q)
 {
     const mDNSu32 slot = HashSlot(&q->qname);
     CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
 {
     const mDNSu32 slot = HashSlot(&q->qname);
     CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
@@ -12215,12 +14415,16 @@ mDNSlocal void mDNS_CheckForCachedNSECS(mDNS *const m, DNSQuestion *q)
 
     for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next)
     {
 
     for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next)
     {
-        if (SameNameRecordAnswersQuestion(&rp->resrec, q) &&
-            rp->resrec.RecordType == kDNSRecordTypePacketNegative &&
-            !rp->nsec)
+        if (SameNameRecordAnswersQuestion(&rp->resrec, q))
         {
         {
-            LogInfo("mDNS_CheckForCachedNSECS: Flushing %s", CRDisplayString(m, rp));
-            mDNS_PurgeCacheResourceRecord(m, rp);
+            if (rp->resrec.RecordType != kDNSRecordTypePacketNegative || !rp->nsec)
+            {
+                if (!rp->CRDNSSECQuestion)
+                {
+                    LogInfo("CheckForDNSSECRecords: Flushing %s", CRDisplayString(m, rp));
+                    mDNS_PurgeCacheResourceRecord(m, rp);
+                }
+            }
         }
     }
 }
         }
     }
 }
@@ -12268,12 +14472,44 @@ mDNSexport void DNSServerChangeForQuestion(mDNS *const m, DNSQuestion *q, DNSSer
     }
 }
 
     }
 }
 
+mDNSlocal void SetConfigState(mDNS *const m, mDNSBool delete)
+{
+    McastResolver *mr;
+    DNSServer *ptr;
+
+    if (delete)
+    {
+        for (ptr = m->DNSServers; ptr; ptr = ptr->next)
+        {
+            ptr->penaltyTime = 0;
+            NumUnicastDNSServers--;
+            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;
+    }
+    else
+    {
+        for (ptr = m->DNSServers; ptr; ptr = ptr->next)
+        {
+            ptr->penaltyTime = 0;
+            NumUnicastDNSServers++;
+            ptr->flags &= ~DNSServer_FlagDelete;
+        }
+        for (mr = m->McastResolvers; mr; mr = mr->next)
+            mr->flags &= ~McastResolver_FlagDelete;
+    }
+}
+
 mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m)
 {
     mDNSu32 slot;
     CacheGroup *cg;
     CacheRecord *cr;
 mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m)
 {
     mDNSu32 slot;
     CacheGroup *cg;
     CacheRecord *cr;
-
+    mDNSBool Restart = mDNSfalse;
     mDNSAddr v4, v6, r;
     domainname fqdn;
     DNSServer   *ptr, **p = &m->DNSServers;
     mDNSAddr v4, v6, r;
     domainname fqdn;
     DNSServer   *ptr, **p = &m->DNSServers;
@@ -12283,29 +14519,27 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m)
 
     debugf("uDNS_SetupDNSConfig: entry");
 
 
     debugf("uDNS_SetupDNSConfig: entry");
 
-    // Let the platform layer get the current DNS information
-    // The m->StartWABQueries is set when we get the first domain enumeration query (no need to hit the network
-    // with domain enumeration queries until we actually need that information). Even if it is not set, we still
-    // need to setup the search domains so that we can append them to queries that need them.
-
-    uDNS_SetupSearchDomains(m, m->StartWABQueries ? UDNS_START_WAB_QUERY : 0);
+    // Let the platform layer get the current DNS information and setup the WAB queries if needed.
+    uDNS_SetupWABQueries(m);
 
     mDNS_Lock(m);
 
 
     mDNS_Lock(m);
 
-    for (ptr = m->DNSServers; ptr; ptr = ptr->next)
-    {
-        ptr->penaltyTime = 0;
-        ptr->flags |= DNSServer_FlagDelete;
+    // We need to first mark all the entries to be deleted. If the configuration changed, then
+    // the entries would be undeleted appropriately. Otherwise, we need to clear them.
+    //
+    // Note: The last argument to mDNSPlatformSetDNSConfig is "mDNStrue" which means ack the
+    // configuration. We already processed search domains in uDNS_SetupWABQueries above and
+    // hence we are ready to ack the configuration as this is the last call to mDNSPlatformSetConfig
+    // for the dns configuration change notification.
+    SetConfigState(m, mDNStrue);
+    if (!mDNSPlatformSetDNSConfig(m, mDNStrue, mDNSfalse, &fqdn, mDNSNULL, mDNSNULL, mDNStrue))
+    {
+        SetConfigState(m, mDNSfalse);
+        mDNS_Unlock(m);
+        LogInfo("uDNS_SetupDNSConfig: No configuration change");
+        return mStatus_NoError;
     }
 
     }
 
-    // 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
     // 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
@@ -12354,6 +14588,7 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m)
     //   non-scoped question and vice versa.
     //
     for (q = m->Questions; q; q=q->next)
     //   non-scoped question and vice versa.
     //
     for (q = m->Questions; q; q=q->next)
+    {
         if (!mDNSOpaque16IsZero(q->TargetQID))
         {
             DNSServer *s, *t;
         if (!mDNSOpaque16IsZero(q->TargetQID))
         {
             DNSServer *s, *t;
@@ -12365,26 +14600,54 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m)
             t = q->qDNSServer;
             if (t != s)
             {
             t = q->qDNSServer;
             if (t != s)
             {
+                mDNSBool old, new;
                 // If DNS Server for this question has changed, reactivate it
                 LogInfo("uDNS_SetupDNSConfig: Updating DNS Server from %#a:%d (%##s) to %#a:%d (%##s) for question %##s (%s) (scope:%p)",
                         t ? &t->addr : mDNSNULL, mDNSVal16(t ? t->port : zeroIPPort), t ? t->domain.c : (mDNSu8*)"",
                         s ? &s->addr : mDNSNULL, mDNSVal16(s ? s->port : zeroIPPort), s ? s->domain.c : (mDNSu8*)"",
                         q->qname.c, DNSTypeName(q->qtype), q->InterfaceID);
 
                 // If DNS Server for this question has changed, reactivate it
                 LogInfo("uDNS_SetupDNSConfig: Updating DNS Server from %#a:%d (%##s) to %#a:%d (%##s) for question %##s (%s) (scope:%p)",
                         t ? &t->addr : mDNSNULL, mDNSVal16(t ? t->port : zeroIPPort), t ? t->domain.c : (mDNSu8*)"",
                         s ? &s->addr : mDNSNULL, mDNSVal16(s ? s->port : zeroIPPort), s ? s->domain.c : (mDNSu8*)"",
                         q->qname.c, DNSTypeName(q->qtype), q->InterfaceID);
 
-                DNSServerChangeForQuestion(m, q, s);
-                q->unansweredQueries = 0;
-                // We still need to pick a new DNSServer for the questions that have been
-                // suppressed, but it is wrong to activate the query as DNS server change
-                // could not possibly change the status of SuppressUnusable questions
-                if (!QuerySuppressed(q))
-                {
-                    debugf("uDNS_SetupDNSConfig: Activating query %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype));
-                    ActivateUnicastQuery(m, q, mDNStrue);
-                    // ActivateUnicastQuery is called for duplicate questions also as it does something
-                    // special for AutoTunnel questions
+                old = q->SuppressQuery;
+                new = ShouldSuppressUnicastQuery(m, q, s);
+                if (old != new)
+                {
+                    // Changing the DNS server affected the SuppressQuery status. We need to
+                    // deliver RMVs for the previous ADDs (if any) before switching to the new
+                    // DNSServer.  To keep it simple, we walk all the questions and mark them
+                    // to be restarted and then handle all of them at once.
+                    q->Restart = 1;
+                    q->SuppressQuery = new;
                     for (qptr = q->next ; qptr; qptr = qptr->next)
                     {
                     for (qptr = q->next ; qptr; qptr = qptr->next)
                     {
-                        if (qptr->DuplicateOf == q) ActivateUnicastQuery(m, qptr, mDNStrue);
+                        if (qptr->DuplicateOf == q)
+                            qptr->Restart = 1;
+                    }
+                    Restart = mDNStrue;
+                }
+                else
+                {
+                    DNSServerChangeForQuestion(m, q, s);
+                    q->unansweredQueries = 0;
+
+                    // If we had sent a query out to DNSServer "t" and we are changing to "s", we
+                    // need to ignore the responses coming back from "t" as the DNS configuration
+                    // has changed e.g., when a new interface is coming up and that becomes the primary
+                    // interface, we switch to the DNS servers configured for the primary interface. In
+                    // this case, we should not accept responses associated with the previous interface as
+                    // the "name" could resolve differently on this new primary interface. Hence, discard
+                    // in-flight responses.
+                    q->TargetQID = mDNS_NewMessageID(m);
+
+                    if (!QuerySuppressed(q))
+                    {
+                        debugf("uDNS_SetupDNSConfig: Activating query %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype));
+                        ActivateUnicastQuery(m, q, mDNStrue);
+                        // ActivateUnicastQuery is called for duplicate questions also as it does something
+                        // special for AutoTunnel questions
+                        for (qptr = q->next ; qptr; qptr = qptr->next)
+                        {
+                            if (qptr->DuplicateOf == q) ActivateUnicastQuery(m, qptr, mDNStrue);
+                        }
                     }
                 }
             }
                     }
                 }
             }
@@ -12396,23 +14659,45 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m)
                     if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = q->qDNSServer; }
             }
         }
                     if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = q->qDNSServer; }
             }
         }
+    }
+    if (Restart)
+        RestartUnicastQuestions(m);
 
     FORALL_CACHERECORDS(slot, cg, cr)
     {
 
     FORALL_CACHERECORDS(slot, cg, cr)
     {
-        if (cr->resrec.InterfaceID) continue;
-        // We just mark them for purge or reconfirm.
+        if (cr->resrec.InterfaceID)
+            continue;
+
+        // We already walked the questions and restarted/reactivated them if the dns server
+        // change affected the question. That should take care of updating the cache. But
+        // what if there is no active question at this point when the DNS server change
+        // happened ? There could be old cache entries lying around and if we don't flush
+        // them, a new question after the DNS server change could pick up these stale
+        // entries and get a wrong answer.
         //
         //
-        // The new DNSServer may be a scoped or non-scoped one. We use the active question's
-        // InterfaceID for looking up the right DNS server
-        ptr = GetServerForName(m, cr->resrec.name, cr->CRActiveQuestion ? cr->CRActiveQuestion->InterfaceID : mDNSNULL);
+        // For cache entries that have active questions we might have skipped rescheduling
+        // the questions if they were suppressed (see above). To keep it simple, we walk
+        // all the cache entries to make sure that there are no stale entries. We use the
+        // active question's InterfaceID/ServiceID for looking up the right DNS server.
+        // Note that the unscoped value for ServiceID is -1.
+        //
+        // Note: If GetServerForName returns NULL, it could either mean that there are no
+        // DNS servers or no matching DNS servers for this question. In either case,
+        // the cache should get purged below when we process deleted DNS servers.
+
+        ptr = GetServerForName(m, cr->resrec.name,
+                               (cr->CRActiveQuestion ? cr->CRActiveQuestion->InterfaceID : mDNSNULL),
+                               (cr->CRActiveQuestion ? cr->CRActiveQuestion->ServiceID   : -1));
 
         // Purge or Reconfirm if this cache entry would use the new DNS server
         if (ptr && (ptr != cr->resrec.rDNSServer))
         {
             // As the DNSServers for this cache record is not the same anymore, we don't
             // want any new questions to pick this old value. If there is no active question,
 
         // Purge or Reconfirm if this cache entry would use the new DNS server
         if (ptr && (ptr != cr->resrec.rDNSServer))
         {
             // As the DNSServers for this cache record is not the same anymore, we don't
             // want any new questions to pick this old value. If there is no active question,
-            // we can't possibly re-confirm, so purge in that case.
-            if (cr->CRActiveQuestion == mDNSNULL)
+            // we can't possibly re-confirm, so purge in that case. If it is a DNSSEC question,
+            // purge the cache as the DNSSEC capabilities of the DNS server may have changed.
+
+            if (cr->CRActiveQuestion == mDNSNULL || DNSSECQuestion(cr->CRActiveQuestion))
             {
                 LogInfo("uDNS_SetupDNSConfig: Purging Resourcerecord %s, New DNS server %#a , Old DNS server %#a", CRDisplayString(m, cr),
                         &ptr->addr, (cr->resrec.rDNSServer != mDNSNULL ?  &cr->resrec.rDNSServer->addr : mDNSNULL));
             {
                 LogInfo("uDNS_SetupDNSConfig: Purging Resourcerecord %s, New DNS server %#a , Old DNS server %#a", CRDisplayString(m, cr),
                         &ptr->addr, (cr->resrec.rDNSServer != mDNSNULL ?  &cr->resrec.rDNSServer->addr : mDNSNULL));
@@ -12480,9 +14765,8 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m)
                 }
             }
             *p = (*p)->next;
                 }
             }
             *p = (*p)->next;
-            debugf("uDNS_SetupDNSConfig: Deleting server %p %#a:%d (%##s)", ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c);
+            LogInfo("uDNS_SetupDNSConfig: Deleting server %p %#a:%d (%##s) %d", ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c, NumUnicastDNSServers);
             mDNSPlatformMemFree(ptr);
             mDNSPlatformMemFree(ptr);
-            NumUnicastDNSServers--;
         }
         else
         {
         }
         else
         {
@@ -12645,6 +14929,7 @@ mDNSexport void mDNS_StartExit(mDNS *const m)
         // Clearing t->ExternalPort/t->RequestedPort will cause the mStatus_MemFree callback handlers
         // to not do this.
         t->ExternalAddress = zerov4Addr;
         // Clearing t->ExternalPort/t->RequestedPort will cause the mStatus_MemFree callback handlers
         // to not do this.
         t->ExternalAddress = zerov4Addr;
+        t->NewAddress      = zerov4Addr;
         t->ExternalPort    = zeroIPPort;
         t->RequestedPort   = zeroIPPort;
         t->Lifetime        = 0;
         t->ExternalPort    = zeroIPPort;
         t->RequestedPort   = zeroIPPort;
         t->Lifetime        = 0;
index 7b6c2c673f530c65a8b666c9f9aadee2b9791422..5467ae64333dedd59932d46a5629eba16de87010 100755 (executable)
@@ -138,6 +138,8 @@ extern void verbosedebugf_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1
 
 extern int mDNS_LoggingEnabled;
 extern int mDNS_PacketLoggingEnabled;
 
 extern int mDNS_LoggingEnabled;
 extern int mDNS_PacketLoggingEnabled;
+extern int mDNS_McastLoggingEnabled;
+extern int mDNS_McastTracingEnabled;
 extern int mDNS_DebugMode;          // If non-zero, LogMsg() writes to stderr instead of syslog
 extern const char ProgramName[];
 
 extern int mDNS_DebugMode;          // If non-zero, LogMsg() writes to stderr instead of syslog
 extern const char ProgramName[];
 
index 3eeb88ebf030cfa62a62f8ab65fa5daf09e90bfc..b041f0711c6460e369f046536a9533e03ce9ecd6 100755 (executable)
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 4 -*-
  *
 /* -*- Mode: C; tab-width: 4 -*-
  *
- * Copyright (c) 2002-2012 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2002-2013 Apple Computer, 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.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -51,8 +51,8 @@
 
  */
 
 
  */
 
-#ifndef __mDNSClientAPI_h
-#define __mDNSClientAPI_h
+#ifndef __mDNSEmbeddedAPI_h
+#define __mDNSEmbeddedAPI_h
 
 #if defined(EFI32) || defined(EFI64) || defined(EFIX64)
 // EFI doesn't have stdarg.h unless it's building with GCC.
 
 #if defined(EFI32) || defined(EFI64) || defined(EFIX64)
 // EFI doesn't have stdarg.h unless it's building with GCC.
 extern "C" {
 #endif
 
 extern "C" {
 #endif
 
+// ***************************************************************************
+// Feature removal compile options & limited resource targets
+
+// The following compile options are responsible for removing certain features from mDNSCore to reduce the
+// memory footprint for use in embedded systems with limited resources.
+
+// UNICAST_DISABLED - disables unicast DNS functionality, including Wide Area Bonjour
+// ANONYMOUS_DISABLED - disables anonymous functionality
+// DNSSEC_DISABLED - disables DNSSEC functionality
+// SPC_DISABLED - disables Bonjour Sleep Proxy client
+// IDLESLEEPCONTROL_DISABLED - disables sleep control for Bonjour Sleep Proxy clients
+
+// In order to disable the above features pass the option to your compiler, e.g. -D UNICAST_DISABLED
+
+// Additionally, the LIMITED_RESOURCES_TARGET compile option will eliminate caching and
+// and reduce the maximum DNS message sizes.
+
+#ifdef LIMITED_RESOURCES_TARGET
+// Don't support jumbo frames
+#define AbsoluteMaxDNSMessageData      1500
+// StandardAuthRDSize is 264 (256+8), which is large enough to hold a maximum-sized SRV record (6 + 256 bytes)
+#define MaximumRDSize                          264
+// Don't cache anything
+#define AUTH_HASH_SLOTS                        1
+#define CACHE_HASH_SLOTS                       1
+#endif
+
 // ***************************************************************************
 // Function scope indicators
 
 // ***************************************************************************
 // Function scope indicators
 
@@ -220,31 +247,21 @@ typedef enum                // From RFC 1035
 
 // mDNS defines its own names for these common types to simplify portability across
 // multiple platforms that may each have their own (different) names for these types.
 
 // mDNS defines its own names for these common types to simplify portability across
 // multiple platforms that may each have their own (different) names for these types.
-typedef          int mDNSBool;
+typedef unsigned char mDNSBool;
 typedef   signed char mDNSs8;
 typedef unsigned char mDNSu8;
 typedef   signed short mDNSs16;
 typedef unsigned short mDNSu16;
 
 typedef   signed char mDNSs8;
 typedef unsigned char mDNSu8;
 typedef   signed short mDNSs16;
 typedef unsigned short mDNSu16;
 
-// <http://gcc.gnu.org/onlinedocs/gcc-3.3.3/cpp/Common-Predefined-Macros.html> says
-//   __LP64__ _LP64
-//   These macros are defined, with value 1, if (and only if) the compilation is
-//   for a target where long int and pointer both use 64-bits and int uses 32-bit.
-// <http://www.intel.com/software/products/compilers/clin/docs/ug/lin1077.htm> says
-//   Macro Name __LP64__ Value 1
-// A quick Google search for "defined(__LP64__)" OR "#ifdef __LP64__" gives 2590 hits and
-// a search for "#if __LP64__" gives only 12, so I think we'll go with the majority and use defined()
+// Source: http://www.unix.org/version2/whatsnew/lp64_wp.html
+// http://software.intel.com/sites/products/documentation/hpc/mkl/lin/MKL_UG_structure/Support_for_ILP64_Programming.htm
+// It can be safely assumed that int is 32bits on the platform
 #if defined(_ILP64) || defined(__ILP64__)
 typedef   signed int32 mDNSs32;
 typedef unsigned int32 mDNSu32;
 #if defined(_ILP64) || defined(__ILP64__)
 typedef   signed int32 mDNSs32;
 typedef unsigned int32 mDNSu32;
-#elif defined(_LP64) || defined(__LP64__)
+#else
 typedef   signed int mDNSs32;
 typedef unsigned int mDNSu32;
 typedef   signed int mDNSs32;
 typedef unsigned int mDNSu32;
-#else
-typedef   signed long mDNSs32;
-typedef unsigned long mDNSu32;
-//typedef   signed int mDNSs32;
-//typedef unsigned int mDNSu32;
 #endif
 
 // To enforce useful type checking, we make mDNSInterfaceID be a pointer to a dummy struct
 #endif
 
 // To enforce useful type checking, we make mDNSInterfaceID be a pointer to a dummy struct
@@ -344,8 +361,8 @@ enum
     mStatus_BadKey                    = -65561,
     mStatus_TransientErr              = -65562,     // transient failures, e.g. sending packets shortly after a network transition or wake from sleep
     mStatus_ServiceNotRunning         = -65563,     // Background daemon not running
     mStatus_BadKey                    = -65561,
     mStatus_TransientErr              = -65562,     // transient failures, e.g. sending packets shortly after a network transition or wake from sleep
     mStatus_ServiceNotRunning         = -65563,     // Background daemon not running
-    mStatus_NATPortMappingUnsupported = -65564,     // NAT doesn't support NAT-PMP or UPnP
-    mStatus_NATPortMappingDisabled    = -65565,     // NAT supports NAT-PMP or UPnP but it's disabled by the administrator
+    mStatus_NATPortMappingUnsupported = -65564,     // NAT doesn't support PCP, NAT-PMP or UPnP
+    mStatus_NATPortMappingDisabled    = -65565,     // NAT supports PCP, NAT-PMP or UPnP, but it's disabled by the administrator
     mStatus_NoRouter                  = -65566,
     mStatus_PollingMode               = -65567,
     mStatus_Timeout                   = -65568,
     mStatus_NoRouter                  = -65566,
     mStatus_PollingMode               = -65567,
     mStatus_Timeout                   = -65568,
@@ -364,6 +381,10 @@ enum
 };
 
 typedef mDNSs32 mStatus;
 };
 
 typedef mDNSs32 mStatus;
+#define MaxIp 5 // Needs to be consistent with MaxInputIf in dns_services.h
+
+typedef enum { q_stop = 0, q_start } q_state;
+typedef enum { reg_stop = 0, reg_start } reg_state;
 
 // RFC 1034/1035 specify that a domain label consists of a length byte plus up to 63 characters
 #define MAX_DOMAIN_LABEL 63
 
 // RFC 1034/1035 specify that a domain label consists of a length byte plus up to 63 characters
 #define MAX_DOMAIN_LABEL 63
@@ -427,6 +448,13 @@ typedef struct { mDNSu8 c[256]; } UTF8str255;       // Null-terminated C string
 #define kStaticCacheTTL 10
 
 #define DefaultTTLforRRType(X) (((X) == kDNSType_A || (X) == kDNSType_AAAA || (X) == kDNSType_SRV) ? kHostNameTTL : kStandardTTL)
 #define kStaticCacheTTL 10
 
 #define DefaultTTLforRRType(X) (((X) == kDNSType_A || (X) == kDNSType_AAAA || (X) == kDNSType_SRV) ? kHostNameTTL : kStandardTTL)
+#define mDNS_KeepaliveRecord(rr) ((rr)->rrtype == kDNSType_NULL && SameDomainLabel(SecondLabel((rr)->name)->c, (mDNSu8 *)"\x0A_keepalive"))
+
+// Number of times keepalives are sent if no ACK is received before waking up the system
+// this is analogous to net.inet.tcp.keepcnt
+#define kKeepaliveRetryCount    10
+// The frequency at which keepalives are retried if no ACK is received
+#define kKeepaliveRetryInterval 30
 
 typedef struct AuthRecord_struct AuthRecord;
 typedef struct ServiceRecordSet_struct ServiceRecordSet;
 
 typedef struct AuthRecord_struct AuthRecord;
 typedef struct ServiceRecordSet_struct ServiceRecordSet;
@@ -438,6 +466,7 @@ typedef struct ZoneData_struct ZoneData;
 typedef struct mDNS_struct mDNS;
 typedef struct mDNS_PlatformSupport_struct mDNS_PlatformSupport;
 typedef struct NATTraversalInfo_struct NATTraversalInfo;
 typedef struct mDNS_struct mDNS;
 typedef struct mDNS_PlatformSupport_struct mDNS_PlatformSupport;
 typedef struct NATTraversalInfo_struct NATTraversalInfo;
+typedef struct ResourceRecord_struct ResourceRecord;
 
 // Structure to abstract away the differences between TCP/SSL sockets, and one for UDP sockets
 // The actual definition of these structures appear in the appropriate platform support code
 
 // Structure to abstract away the differences between TCP/SSL sockets, and one for UDP sockets
 // The actual definition of these structures appear in the appropriate platform support code
@@ -467,7 +496,9 @@ typedef packedstruct
 // We can send and receive packets up to 9000 bytes (Ethernet Jumbo Frame size, if that ever becomes widely used)
 // However, in the normal case we try to limit packets to 1500 bytes so that we don't get IP fragmentation on standard Ethernet
 // 40 (IPv6 header) + 8 (UDP header) + 12 (DNS message header) + 1440 (DNS message body) = 1500 total
 // We can send and receive packets up to 9000 bytes (Ethernet Jumbo Frame size, if that ever becomes widely used)
 // However, in the normal case we try to limit packets to 1500 bytes so that we don't get IP fragmentation on standard Ethernet
 // 40 (IPv6 header) + 8 (UDP header) + 12 (DNS message header) + 1440 (DNS message body) = 1500 total
+#ifndef AbsoluteMaxDNSMessageData
 #define AbsoluteMaxDNSMessageData 8940
 #define AbsoluteMaxDNSMessageData 8940
+#endif
 #define NormalMaxDNSMessageData 1440
 typedef packedstruct
 {
 #define NormalMaxDNSMessageData 1440
 typedef packedstruct
 {
@@ -597,6 +628,12 @@ typedef packedstruct
     // Typically 8 bytes of options are also present
 } IPv6NDP;                  // 24 bytes or more; IP protocol type 0x3A
 
     // Typically 8 bytes of options are also present
 } IPv6NDP;                  // 24 bytes or more; IP protocol type 0x3A
 
+typedef struct
+{
+    mDNSAddr    ipaddr;
+    char        ethaddr[18];
+} IPAddressMACMapping;
+
 #define NDP_Sol 0x87
 #define NDP_Adv 0x88
 
 #define NDP_Sol 0x87
 #define NDP_Adv 0x88
 
@@ -742,9 +779,12 @@ typedef packedstruct
 
 #define CRYPTO_ALG_MAX              0x0B
 
 
 #define CRYPTO_ALG_MAX              0x0B
 
-// alg - same as in RRSIG, DNS KEY or DS
+// alg - same as in RRSIG, DNS KEY or DS.
 // RFC 4034 defines SHA1
 // RFC 4509 defines SHA256
 // RFC 4034 defines SHA1
 // RFC 4509 defines SHA256
+// Note: NSEC3 also uses 1 for SHA1 and hence we will reuse for now till a new
+// value is assigned.
+//
 #define SHA1_DIGEST_TYPE        1
 #define SHA256_DIGEST_TYPE      2
 #define DIGEST_TYPE_MAX         3
 #define SHA1_DIGEST_TYPE        1
 #define SHA256_DIGEST_TYPE      2
 #define DIGEST_TYPE_MAX         3
@@ -812,16 +852,58 @@ typedef packedstruct
     mDNSu8  *data;
 } rdataDNSKey;
 
     mDNSu8  *data;
 } rdataDNSKey;
 
+#define NSEC3_FIXED_SIZE          5
+#define NSEC3_FLAGS_OPTOUT        1
+#define NSEC3_MAX_ITERATIONS      2500
+typedef packedstruct
+{
+    mDNSu8 alg;
+    mDNSu8 flags;
+    mDNSu16 iterations;
+    mDNSu8 saltLength;
+    mDNSu8 *salt;
+    // hashLength, nxt, bitmap
+} rdataNSEC3;
+
+// In the multicast usage of NSEC3, we know the actual size of RData
+// 4 bytes : HashAlg, Flags,Iterations
+// 5 bytes : Salt Length 1 byte, Salt 4 bytes
+// 21 bytes : HashLength 1 byte, Hash 20 bytes
+// 34 bytes : Window number, Bitmap length, Type bit map to include the first 256 types
+#define MCAST_NSEC3_RDLENGTH (4 + 5 + 21 + 34)
+#define SHA1_HASH_LENGTH 20
+
+// Base32 encoding takes 5 bytes of the input and encodes as 8 bytes of output.
+// For example, SHA-1 hash of 20 bytes will be encoded as 20/5 * 8 = 32 base32
+// bytes. For a max domain name size of 255 bytes of base32 encoding : (255/8)*5
+// is the max hash length possible.
+#define NSEC3_MAX_HASH_LEN     155
+// In NSEC3, the names are hashed and stored in the first label and hence cannot exceed label
+// size.
+#define NSEC3_MAX_B32_LEN      MAX_DOMAIN_LABEL
+
 // We define it here instead of dnssec.h so that these values can be used
 // in files without bringing in all of dnssec.h unnecessarily.
 typedef enum
 {
     DNSSEC_Secure = 1,      // Securely validated and has a chain up to the trust anchor
     DNSSEC_Insecure,        // Cannot build a chain up to the trust anchor
 // We define it here instead of dnssec.h so that these values can be used
 // in files without bringing in all of dnssec.h unnecessarily.
 typedef enum
 {
     DNSSEC_Secure = 1,      // Securely validated and has a chain up to the trust anchor
     DNSSEC_Insecure,        // Cannot build a chain up to the trust anchor
-    DNSSEC_Indeterminate,   // Cannot fetch DNSSEC RRs
-    DNSSEC_Bogus            // failed to validate signatures
+    DNSSEC_Indeterminate,   // Not used currently
+    DNSSEC_Bogus,           // failed to validate signatures
+    DNSSEC_NoResponse       // No DNSSEC records to start with
 } DNSSECStatus;
 
 } DNSSECStatus;
 
+#define DNSSECRecordType(rrtype) (((rrtype) == kDNSType_RRSIG) || ((rrtype) == kDNSType_NSEC) || ((rrtype) == kDNSType_DNSKEY) || ((rrtype) == kDNSType_DS) || \
+                                  ((rrtype) == kDNSType_NSEC3))
+
+typedef enum
+{
+    platform_OSX = 1,   // OSX Platform 
+    platform_iOS,       // iOS Platform 
+    platform_Atv,       // Atv Platform 
+    platform_NonApple   // Non-Apple (Windows, POSIX) Platform
+} Platform_t;
+
 // EDNS Option Code registrations are recorded in the "DNS EDNS0 Options" section of
 // <http://www.iana.org/assignments/dns-parameters>
 
 // EDNS Option Code registrations are recorded in the "DNS EDNS0 Options" section of
 // <http://www.iana.org/assignments/dns-parameters>
 
@@ -829,6 +911,7 @@ typedef enum
 #define kDNSOpt_Lease 2
 #define kDNSOpt_NSID  3
 #define kDNSOpt_Owner 4
 #define kDNSOpt_Lease 2
 #define kDNSOpt_NSID  3
 #define kDNSOpt_Owner 4
+#define kDNSOpt_Trace 65001  // 65001-65534 Reserved for Local/Experimental Use 
 
 typedef struct
 {
 
 typedef struct
 {
@@ -849,19 +932,27 @@ typedef struct
     mDNSOpaque48 password;  // Optional password
 } OwnerOptData;
 
     mDNSOpaque48 password;  // Optional password
 } OwnerOptData;
 
+typedef struct
+{
+    mDNSu8    platf;       // Running platform (see enum Platform_t)
+    mDNSu16   mDNSv;       // mDNSResponder Version (defined in dns_sd.h)
+} TracerOptData;
+
 // Note: rdataOPT format may be repeated an arbitrary number of times in a single resource record
 typedef packedstruct
 {
     mDNSu16 opt;
     mDNSu16 optlen;
 // Note: rdataOPT format may be repeated an arbitrary number of times in a single resource record
 typedef packedstruct
 {
     mDNSu16 opt;
     mDNSu16 optlen;
-    union { LLQOptData llq; mDNSu32 updatelease; OwnerOptData owner; } u;
+    union { LLQOptData llq; mDNSu32 updatelease; OwnerOptData owner; TracerOptData tracer; } u;
 } rdataOPT;
 
 // Space needed to put OPT records into a packet:
 } rdataOPT;
 
 // Space needed to put OPT records into a packet:
-// Header      11 bytes (name 1, type 2, class 2, TTL 4, length 2)
-// LLQ rdata   18 bytes (opt 2, len 2, vers 2, op 2, err 2, id 8, lease 4)
-// Lease rdata  8 bytes (opt 2, len 2, lease 4)
-// Owner rdata 12-24    (opt 2, len 2, owner 8-20)
+// Header         11  bytes (name 1, type 2, class 2, TTL 4, length 2)
+// LLQ   rdata    18  bytes (opt 2, len 2, vers 2, op 2, err 2, id 8, lease 4)
+// Lease rdata     8  bytes (opt 2, len 2, lease 4)
+// Owner rdata 12-24  bytes (opt 2, len 2, owner 8-20)
+// Trace rdata     7  bytes (opt 2, len 2, platf 1, mDNSv 2)
+
 
 #define DNSOpt_Header_Space                 11
 #define DNSOpt_LLQData_Space               (4 + 2 + 2 + 2 + 8 + 4)
 
 #define DNSOpt_Header_Space                 11
 #define DNSOpt_LLQData_Space               (4 + 2 + 2 + 2 + 8 + 4)
@@ -870,6 +961,7 @@ typedef packedstruct
 #define DNSOpt_OwnerData_ID_Wake_Space     (4 + 2 + 6 + 6)
 #define DNSOpt_OwnerData_ID_Wake_PW4_Space (4 + 2 + 6 + 6 + 4)
 #define DNSOpt_OwnerData_ID_Wake_PW6_Space (4 + 2 + 6 + 6 + 6)
 #define DNSOpt_OwnerData_ID_Wake_Space     (4 + 2 + 6 + 6)
 #define DNSOpt_OwnerData_ID_Wake_PW4_Space (4 + 2 + 6 + 6 + 4)
 #define DNSOpt_OwnerData_ID_Wake_PW6_Space (4 + 2 + 6 + 6 + 6)
+#define DNSOpt_TraceData_Space             (4 + 1 + 2)
 
 #define ValidOwnerLength(X) (   (X) == DNSOpt_OwnerData_ID_Space          - 4 || \
                                 (X) == DNSOpt_OwnerData_ID_Wake_Space     - 4 || \
 
 #define ValidOwnerLength(X) (   (X) == DNSOpt_OwnerData_ID_Space          - 4 || \
                                 (X) == DNSOpt_OwnerData_ID_Wake_Space     - 4 || \
@@ -881,6 +973,7 @@ typedef packedstruct
 #define DNSOpt_Data_Space(O) (                                  \
         (O)->opt == kDNSOpt_LLQ   ? DNSOpt_LLQData_Space   :        \
         (O)->opt == kDNSOpt_Lease ? DNSOpt_LeaseData_Space :        \
 #define DNSOpt_Data_Space(O) (                                  \
         (O)->opt == kDNSOpt_LLQ   ? DNSOpt_LLQData_Space   :        \
         (O)->opt == kDNSOpt_Lease ? DNSOpt_LeaseData_Space :        \
+        (O)->opt == kDNSOpt_Trace ? DNSOpt_TraceData_Space :        \
         (O)->opt == kDNSOpt_Owner ? DNSOpt_Owner_Space(&(O)->u.owner.HMAC, &(O)->u.owner.IMAC) : 0x10000)
 
 // NSEC record is defined in RFC 4034.
         (O)->opt == kDNSOpt_Owner ? DNSOpt_Owner_Space(&(O)->u.owner.HMAC, &(O)->u.owner.IMAC) : 0x10000)
 
 // NSEC record is defined in RFC 4034.
@@ -893,20 +986,22 @@ typedef packedstruct
 //
 // This would be a waste, as types about 256 are not very common. But it would be odd, if we receive
 // a type above 256 (.US zone had TYPE65534 when this code was written) and not able to handle it.
 //
 // This would be a waste, as types about 256 are not very common. But it would be odd, if we receive
 // a type above 256 (.US zone had TYPE65534 when this code was written) and not able to handle it.
-// Hence, we handle any size by not fixing a strucure in place. The following is just a palceholder
+// Hence, we handle any size by not fixing a strucure in place. The following is just a placeholder
 // and never used anywhere.
 //
 #define NSEC_MCAST_WINDOW_SIZE 32
 typedef struct
 {
 // and never used anywhere.
 //
 #define NSEC_MCAST_WINDOW_SIZE 32
 typedef struct
 {
-    //domainname *next;
-    //char bitmap[32];
+    domainname *next; //placeholders are uncommented because C89 in Windows requires that a struct has at least a member.
+    char bitmap[32];
 } rdataNSEC;
 
 // StandardAuthRDSize is 264 (256+8), which is large enough to hold a maximum-sized SRV record (6 + 256 bytes)
 // MaximumRDSize is 8K the absolute maximum we support (at least for now)
 #define StandardAuthRDSize 264
 } rdataNSEC;
 
 // StandardAuthRDSize is 264 (256+8), which is large enough to hold a maximum-sized SRV record (6 + 256 bytes)
 // MaximumRDSize is 8K the absolute maximum we support (at least for now)
 #define StandardAuthRDSize 264
+#ifndef MaximumRDSize
 #define MaximumRDSize 8192
 #define MaximumRDSize 8192
+#endif
 
 // InlineCacheRDSize is 68
 // Records received from the network with rdata this size or less have their rdata stored right in the CacheRecord object
 
 // InlineCacheRDSize is 68
 // Records received from the network with rdata this size or less have their rdata stored right in the CacheRecord object
@@ -1052,6 +1147,76 @@ typedef packedstruct
     mDNSu32 NATRep_lease;
 } NATPortMapReply;
 
     mDNSu32 NATRep_lease;
 } NATPortMapReply;
 
+// PCP Support for IPv4 mappings
+
+#define PCP_VERS 0x02
+#define PCP_WAITSECS_AFTER_EPOCH_INVALID 5
+
+typedef enum
+{
+    PCPOp_Announce = 0,
+    PCPOp_Map      = 1
+} PCPOp_t;
+
+typedef enum
+{
+    PCPProto_All = 0,
+    PCPProto_TCP = 6,
+    PCPProto_UDP = 17
+} PCPProto_t;
+
+typedef enum
+{
+    PCPResult_Success         = 0,
+    PCPResult_UnsuppVersion   = 1,
+    PCPResult_NotAuthorized   = 2,
+    PCPResult_MalformedReq    = 3,
+    PCPResult_UnsuppOpcode    = 4,
+    PCPResult_UnsuppOption    = 5,
+    PCPResult_MalformedOption = 6,
+    PCPResult_NetworkFailure  = 7,
+    PCPResult_NoResources     = 8,
+    PCPResult_UnsuppProtocol  = 9,
+    PCPResult_UserExQuota     = 10,
+    PCPResult_CantProvideExt  = 11,
+    PCPResult_AddrMismatch    = 12,
+    PCPResult_ExcesRemotePeer = 13
+} PCPResult_t;
+
+typedef packedstruct
+{
+    mDNSu8       version;
+    mDNSu8       opCode;
+    mDNSOpaque16 reserved;
+    mDNSu32      lifetime;
+    mDNSv6Addr   clientAddr;
+    mDNSu32      nonce[3];
+    mDNSu8       protocol;
+    mDNSu8       reservedMapOp[3];
+    mDNSIPPort   intPort;
+    mDNSIPPort   extPort;
+    mDNSv6Addr   extAddress;
+} PCPMapRequest;
+
+typedef packedstruct
+{
+    mDNSu8     version;
+    mDNSu8     opCode;
+    mDNSu8     reserved;
+    mDNSu8     result;
+    mDNSu32    lifetime;
+    mDNSu32    epoch;
+    mDNSu32    clientAddrParts[3];
+    mDNSu32    nonce[3];
+    mDNSu8     protocol;
+    mDNSu8     reservedMapOp[3];
+    mDNSIPPort intPort;
+    mDNSIPPort extPort;
+    mDNSv6Addr extAddress;
+} PCPMapReply;
+
+// LNT Support
+
 typedef enum
 {
     LNTDiscoveryOp      = 1,
 typedef enum
 {
     LNTDiscoveryOp      = 1,
@@ -1084,6 +1249,14 @@ typedef void (*NATTraversalClientCallback)(mDNS *m, NATTraversalInfo *n);
 // if m->timenow <  ExpiryTime then we have an active mapping, and we'll renew halfway to expiry
 // if m->timenow >= ExpiryTime then our mapping has expired, and we're trying to create one
 
 // if m->timenow <  ExpiryTime then we have an active mapping, and we'll renew halfway to expiry
 // if m->timenow >= ExpiryTime then our mapping has expired, and we're trying to create one
 
+typedef enum
+{
+    NATTProtocolNone    = 0,
+    NATTProtocolNATPMP  = 1,
+    NATTProtocolUPNPIGD = 2,
+    NATTProtocolPCP     = 3,
+} NATTProtocol;
+
 struct NATTraversalInfo_struct
 {
     // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them.
 struct NATTraversalInfo_struct
 {
     // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them.
@@ -1093,6 +1266,9 @@ struct NATTraversalInfo_struct
     mDNSs32 retryInterval;                          // Current interval, between last packet we sent and the next one
     mDNSs32 retryPortMap;                           // If Protocol is nonzero, time to send our next mapping packet
     mStatus NewResult;                              // New error code; will be copied to Result just prior to invoking callback
     mDNSs32 retryInterval;                          // Current interval, between last packet we sent and the next one
     mDNSs32 retryPortMap;                           // If Protocol is nonzero, time to send our next mapping packet
     mStatus NewResult;                              // New error code; will be copied to Result just prior to invoking callback
+    NATTProtocol lastSuccessfulProtocol;            // To send correct deletion request & update non-PCP external address operations
+    mDNSBool sentNATPMP;                            // Whether we just sent a NAT-PMP packet, so we won't send another if
+                                                    //    we receive another NAT-PMP "Unsupported Version" packet
 
 #ifdef _LEGACY_NAT_TRAVERSAL_
     tcpLNTInfo tcpInfo;                             // Legacy NAT traversal (UPnP) TCP connection
 
 #ifdef _LEGACY_NAT_TRAVERSAL_
     tcpLNTInfo tcpInfo;                             // Legacy NAT traversal (UPnP) TCP connection
@@ -1107,10 +1283,11 @@ struct NATTraversalInfo_struct
     //     is reported as the same as our InternalPort, since that is effectively our externally-visible port too.
     //     Again, RequestedPort retains the external port we'd like to get the next time we find ourself behind a NAT gateway.
     // To improve stability of port mappings, RequestedPort is updated any time we get a successful
     //     is reported as the same as our InternalPort, since that is effectively our externally-visible port too.
     //     Again, RequestedPort retains the external port we'd like to get the next time we find ourself behind a NAT gateway.
     // To improve stability of port mappings, RequestedPort is updated any time we get a successful
-    // mapping response from the NAT-PMP or UPnP gateway. For example, if we ask for port 80, and
+    // mapping response from the PCP, NAT-PMP or UPnP gateway. For example, if we ask for port 80, and
     // get assigned port 81, then thereafter we'll contine asking for port 81.
     mDNSInterfaceID InterfaceID;
     mDNSv4Addr ExternalAddress;                     // Initially set to onesIPv4Addr, until first callback
     // get assigned port 81, then thereafter we'll contine asking for port 81.
     mDNSInterfaceID InterfaceID;
     mDNSv4Addr ExternalAddress;                     // Initially set to onesIPv4Addr, until first callback
+    mDNSv4Addr NewAddress;                          // May be updated with actual value assigned by gateway
     mDNSIPPort ExternalPort;
     mDNSu32 Lifetime;
     mStatus Result;
     mDNSIPPort ExternalPort;
     mDNSu32 Lifetime;
     mStatus Result;
@@ -1124,6 +1301,12 @@ struct NATTraversalInfo_struct
     void                       *clientContext;
 };
 
     void                       *clientContext;
 };
 
+// ***************************************************************************
+#if 0
+#pragma mark -
+#pragma mark - DNSServer & McastResolver structures and constants
+#endif
+
 enum
 {
     DNSServer_Untested = 0,
 enum
 {
     DNSServer_Untested = 0,
@@ -1153,10 +1336,27 @@ typedef struct McastResolver
     mDNSu32 timeout;            // timeout value for questions
 } McastResolver;
 
     mDNSu32 timeout;            // timeout value for questions
 } McastResolver;
 
+// scoped values for DNSServer matching
+enum
+{
+    kScopeNone         = 0,        // DNS server used by unscoped questions
+    kScopeInterfaceID  = 1,        // Scoped DNS server used only by scoped questions
+    kScopeServiceID    = 2         // Service specific DNS server used only by questions
+                                   // have a matching serviceID
+};
+
+// Note: DNSSECAware is set if we are able to get a valid response to
+// a DNSSEC question. In some cases it is possible that the proxy
+// strips the EDNS0 option and we just get a plain response with no
+// signatures. But we still mark DNSSECAware in that case. As DNSSECAware
+// is only used to determine whether DNSSEC_VALIDATION_SECURE_OPTIONAL
+// should be turned off or not, it is sufficient that we are getting
+// responses back.
 typedef struct DNSServer
 {
     struct DNSServer *next;
 typedef struct DNSServer
 {
     struct DNSServer *next;
-    mDNSInterfaceID interface;  // For specialized uses; we can have DNS servers reachable over specific interfaces
+    mDNSInterfaceID interface;  // DNS requests should be sent on this interface
+    mDNSs32 serviceID;
     mDNSAddr addr;
     mDNSIPPort port;
     mDNSOpaque16 testid;
     mDNSAddr addr;
     mDNSIPPort port;
     mDNSOpaque16 testid;
@@ -1164,15 +1364,29 @@ typedef struct DNSServer
     mDNSu32 teststate;          // Have we sent bug-detection query to this server?
     mDNSs32 lasttest;           // Time we sent last bug-detection query to this server
     domainname domain;          // name->server matching for "split dns"
     mDNSu32 teststate;          // Have we sent bug-detection query to this server?
     mDNSs32 lasttest;           // Time we sent last bug-detection query to this server
     domainname domain;          // name->server matching for "split dns"
-    mDNSs32 penaltyTime;         // amount of time this server is penalized
-    mDNSBool scoped;            // interface should be matched against question only
-                                // if scoped is set
+    mDNSs32 penaltyTime;        // amount of time this server is penalized
+    mDNSu32 scoped;             // See the scoped enum above
     mDNSu32 timeout;            // timeout value for questions
     mDNSBool cellIntf;          // Resolver from Cellular Interface ?
     mDNSu16 resGroupID;         // ID of the resolver group that contains this DNSServer
     mDNSu32 timeout;            // timeout value for questions
     mDNSBool cellIntf;          // Resolver from Cellular Interface ?
     mDNSu16 resGroupID;         // ID of the resolver group that contains this DNSServer
+    mDNSBool req_A;             // If set, send v4 query (DNSConfig allows A queries)
+    mDNSBool req_AAAA;          // If set, send v6 query (DNSConfig allows AAAA queries)
+    mDNSBool req_DO;            // If set, okay to send DNSSEC queries (EDNS DO bit is supported)
+    mDNSBool retransDO;         // Total Retransmissions for queries sent with DO option
+    mDNSBool DNSSECAware;       // set if we are able to receive a response to a request
+                                // sent with DO option.
 } DNSServer;
 
 } DNSServer;
 
-typedef struct                          // Size is 36 bytes when compiling for 32-bit; 48 when compiling for 64-bit
+typedef struct
+{
+    mDNSu8 *AnonData;
+    int AnonDataLen;
+    mDNSu32 salt;
+    ResourceRecord *nsec3RR;
+    mDNSInterfaceID SendNow;     // The interface ID that this record should be sent on
+} AnonymousInfo;
+
+struct ResourceRecord_struct
 {
     mDNSu8 RecordType;                  // See enum above
     mDNSu16 rrtype;
 {
     mDNSu8 RecordType;                  // See enum above
     mDNSu16 rrtype;
@@ -1197,7 +1411,9 @@ typedef struct                          // Size is 36 bytes when compiling for 3
     const domainname *name;
     RData           *rdata;             // Pointer to storage for this rdata
     DNSServer       *rDNSServer;        // Unicast DNS server authoritative for this entry;null for multicast
     const domainname *name;
     RData           *rdata;             // Pointer to storage for this rdata
     DNSServer       *rDNSServer;        // Unicast DNS server authoritative for this entry;null for multicast
-} ResourceRecord;
+    AnonymousInfo   *AnonInfo;          // Anonymous Information
+};
+
 
 // Unless otherwise noted, states may apply to either independent record registrations or service registrations
 typedef enum
 
 // Unless otherwise noted, states may apply to either independent record registrations or service registrations
 typedef enum
@@ -1239,7 +1455,9 @@ struct AuthGroup_struct             // Header object for a list of AuthRecords w
     mDNSu8 namestorage[AUTH_GROUP_NAME_SIZE];
 };
 
     mDNSu8 namestorage[AUTH_GROUP_NAME_SIZE];
 };
 
+#ifndef AUTH_HASH_SLOTS
 #define AUTH_HASH_SLOTS 499
 #define AUTH_HASH_SLOTS 499
+#endif
 #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)                                                                         \
 #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)                                                                         \
@@ -1262,10 +1480,16 @@ typedef enum
     AuthRecordAny,              // registered for *Any, NOT including P2P interfaces
     AuthRecordAnyIncludeP2P,    // registered for *Any, including P2P interfaces
     AuthRecordAnyIncludeAWDL,   // registered for *Any, including AWDL interface
     AuthRecordAny,              // registered for *Any, NOT including P2P interfaces
     AuthRecordAnyIncludeP2P,    // registered for *Any, including P2P interfaces
     AuthRecordAnyIncludeAWDL,   // registered for *Any, including AWDL interface
+    AuthRecordAnyIncludeAWDLandP2P, // registered for *Any, including AWDL and P2P interfaces
     AuthRecordLocalOnly,
     AuthRecordP2P               // discovered over D2D/P2P framework
 } AuthRecType;
 
     AuthRecordLocalOnly,
     AuthRecordP2P               // discovered over D2D/P2P framework
 } AuthRecType;
 
+typedef enum
+{
+    AuthFlagsWakeOnly = 0x1     // WakeOnly service
+} AuthRecordFlags;
+
 struct AuthRecord_struct
 {
     // For examples of how to set up this structure for use in mDNS_Register(),
 struct AuthRecord_struct
 {
     // For examples of how to set up this structure for use in mDNS_Register(),
@@ -1287,6 +1511,7 @@ struct AuthRecord_struct
     mDNSu8 AutoTarget;                  // Set if the target of this record (PTR, CNAME, SRV, etc.) is our host name
     mDNSu8 AllowRemoteQuery;            // Set if we allow hosts not on the local link to query this record
     mDNSu8 ForceMCast;                  // Set by client to advertise solely via multicast, even for apparently unicast names
     mDNSu8 AutoTarget;                  // Set if the target of this record (PTR, CNAME, SRV, etc.) is our host name
     mDNSu8 AllowRemoteQuery;            // Set if we allow hosts not on the local link to query this record
     mDNSu8 ForceMCast;                  // Set by client to advertise solely via multicast, even for apparently unicast names
+    mDNSu8 AuthFlags;
 
     OwnerOptData WakeUp;                // WakeUp.HMAC.l[0] nonzero indicates that this is a Sleep Proxy record
     mDNSAddr AddressProxy;              // For reverse-mapping Sleep Proxy PTR records, address in question
 
     OwnerOptData WakeUp;                // WakeUp.HMAC.l[0] nonzero indicates that this is a Sleep Proxy record
     mDNSAddr AddressProxy;              // For reverse-mapping Sleep Proxy PTR records, address in question
@@ -1297,6 +1522,7 @@ struct AuthRecord_struct
 
     // Field Group 3: Transient state for Authoritative Records
     mDNSu8 Acknowledged;                // Set if we've given the success callback to the client
 
     // Field Group 3: Transient state for Authoritative Records
     mDNSu8 Acknowledged;                // Set if we've given the success callback to the client
+    mDNSu8 ProbeRestartCount;           // Number of times we have restarted probing
     mDNSu8 ProbeCount;                  // Number of probes remaining before this record is valid (kDNSRecordTypeUnique)
     mDNSu8 AnnounceCount;               // Number of announcements remaining (kDNSRecordTypeShared)
     mDNSu8 RequireGoodbye;              // Set if this RR has been announced on the wire and will require a goodbye packet
     mDNSu8 ProbeCount;                  // Number of probes remaining before this record is valid (kDNSRecordTypeUnique)
     mDNSu8 AnnounceCount;               // Number of announcements remaining (kDNSRecordTypeShared)
     mDNSu8 RequireGoodbye;              // Set if this RR has been announced on the wire and will require a goodbye packet
@@ -1376,16 +1602,17 @@ struct AuthRecord_struct
 // Note: Question_uDNS(Q) is used in *only* one place -- on entry to mDNS_StartQuery_internal, to decide whether to set TargetQID.
 // Everywhere else in the code, the determination of whether a question is unicast is made by checking to see if TargetQID is nonzero.
 #define AuthRecord_uDNS(R) ((R)->resrec.InterfaceID == mDNSInterface_Any && !(R)->ForceMCast && !IsLocalDomain((R)->resrec.name))
 // Note: Question_uDNS(Q) is used in *only* one place -- on entry to mDNS_StartQuery_internal, to decide whether to set TargetQID.
 // Everywhere else in the code, the determination of whether a question is unicast is made by checking to see if TargetQID is nonzero.
 #define AuthRecord_uDNS(R) ((R)->resrec.InterfaceID == mDNSInterface_Any && !(R)->ForceMCast && !IsLocalDomain((R)->resrec.name))
-#define Question_uDNS(Q)   ((Q)->InterfaceID == mDNSInterface_Unicast || \
+#define Question_uDNS(Q)   ((Q)->InterfaceID == mDNSInterface_Unicast || (Q)->ProxyQuestion || \
                             ((Q)->InterfaceID != mDNSInterface_LocalOnly && (Q)->InterfaceID != mDNSInterface_P2P && !(Q)->ForceMCast && !IsLocalDomain(&(Q)->qname)))
 
 #define RRLocalOnly(rr) ((rr)->ARType == AuthRecordLocalOnly || (rr)->ARType == AuthRecordP2P)
 
                             ((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 || (rr)->ARType == AuthRecordAnyIncludeAWDL)
+#define RRAny(rr) ((rr)->ARType == AuthRecordAny || (rr)->ARType == AuthRecordAnyIncludeP2P || (rr)->ARType == AuthRecordAnyIncludeAWDL || (rr)->ARType == AuthRecordAnyIncludeAWDLandP2P)
 
 // Question (A or AAAA) that is suppressed currently because IPv4 or IPv6 address
 
 // 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)
+// is not available locally for A or AAAA question respectively. Also, if the
+// query is disallowed for the "pid" that we are sending on behalf of, suppress it.
+#define QuerySuppressed(Q) (((Q)->SuppressUnusable && (Q)->SuppressQuery) || ((Q)->DisallowPID))
 
 #define PrivateQuery(Q) ((Q)->AuthInfo && (Q)->AuthInfo->AutoTunnel)
 
 
 #define PrivateQuery(Q) ((Q)->AuthInfo && (Q)->AuthInfo->AutoTunnel)
 
@@ -1414,8 +1641,9 @@ struct CacheRecord_struct
     mDNSs32 LastUsed;                   // In platform time units
     DNSQuestion    *CRActiveQuestion;   // Points to an active question referencing this answer. Can never point to a NewQuestion.
     mDNSs32 LastUnansweredTime;         // In platform time units; last time we incremented UnansweredQueries
     mDNSs32 LastUsed;                   // In platform time units
     DNSQuestion    *CRActiveQuestion;   // Points to an active question referencing this answer. Can never point to a NewQuestion.
     mDNSs32 LastUnansweredTime;         // In platform time units; last time we incremented UnansweredQueries
-    mDNSu16 UnansweredQueries;          // Number of times we've issued a query for this record without getting an answer
-    mDNSu16 rcode;                      // Error code needed for NSEC proofs
+    mDNSu8  UnansweredQueries;          // Number of times we've issued a query for this record without getting an answer
+    mDNSu8  CRDNSSECQuestion;           // Set to 1 if this was created in response to a DNSSEC question
+    mDNSOpaque16 responseFlags;         // Second 16 bit in the DNS response
 #if ENABLE_MULTI_PACKET_QUERY_SNOOPING
     mDNSu32 MPUnansweredQ;              // Multi-packet query handling: Number of times we've seen a query for this record
     mDNSs32 MPLastUnansweredQT;         // Multi-packet query handling: Last time we incremented MPUnansweredQ
 #if ENABLE_MULTI_PACKET_QUERY_SNOOPING
     mDNSu32 MPUnansweredQ;              // Multi-packet query handling: Number of times we've seen a query for this record
     mDNSs32 MPLastUnansweredQT;         // Multi-packet query handling: Last time we incremented MPUnansweredQ
@@ -1424,6 +1652,7 @@ struct CacheRecord_struct
 #endif
     CacheRecord    *NextInCFList;       // Set if this is in the list of records we just received with the cache flush bit set
     CacheRecord    *nsec;               // NSEC records needed for non-existence proofs
 #endif
     CacheRecord    *NextInCFList;       // Set if this is in the list of records we just received with the cache flush bit set
     CacheRecord    *nsec;               // NSEC records needed for non-existence proofs
+    CacheRecord    *soa;                // SOA record to return for proxy questions
 
     mDNSAddr sourceAddress;             // node from which we received this record
     // Size to here is 76 bytes when compiling 32-bit; 104 bytes when compiling 64-bit
 
     mDNSAddr sourceAddress;             // node from which we received this record
     // Size to here is 76 bytes when compiling 32-bit; 104 bytes when compiling 64-bit
@@ -1511,6 +1740,9 @@ struct ServiceRecordSet_struct
     ExtraResourceRecord *Extras;    // Optional list of extra AuthRecords attached to this service registration
     mDNSu32 NumSubTypes;
     AuthRecord          *SubTypes;
     ExtraResourceRecord *Extras;    // Optional list of extra AuthRecords attached to this service registration
     mDNSu32 NumSubTypes;
     AuthRecord          *SubTypes;
+    const mDNSu8        *AnonData;
+    mDNSu32             flags;      // saved for subsequent calls to mDNS_RegisterService() if records 
+                                    // need to be re-registered.
     AuthRecord RR_ADV;              // e.g. _services._dns-sd._udp.local. PTR _printer._tcp.local.
     AuthRecord RR_PTR;              // e.g. _printer._tcp.local.        PTR Name._printer._tcp.local.
     AuthRecord RR_SRV;              // e.g. Name._printer._tcp.local.   SRV 0 0 port target
     AuthRecord RR_ADV;              // e.g. _services._dns-sd._udp.local. PTR _printer._tcp.local.
     AuthRecord RR_PTR;              // e.g. _printer._tcp.local.        PTR Name._printer._tcp.local.
     AuthRecord RR_SRV;              // e.g. Name._printer._tcp.local.   SRV 0 0 port target
@@ -1609,8 +1841,11 @@ typedef struct DomainAuthInfo
 // layer. These values are used within mDNSResponder and not sent across to the application. QC_addnocache is for
 // delivering a response without adding to the cache. QC_forceresponse is superset of QC_addnocache where in
 // addition to not entering in the cache, it also forces the negative response through.
 // layer. These values are used within mDNSResponder and not sent across to the application. QC_addnocache is for
 // delivering a response without adding to the cache. QC_forceresponse is superset of QC_addnocache where in
 // addition to not entering in the cache, it also forces the negative response through.
-typedef enum { QC_rmv = 0, QC_add, QC_addnocache, QC_forceresponse, QC_dnssec } QC_result;
+typedef enum { QC_rmv = 0, QC_add, QC_addnocache, QC_forceresponse, QC_dnssec , QC_nodnssec, QC_suppressed } QC_result;
 typedef void mDNSQuestionCallback (mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord);
 typedef void mDNSQuestionCallback (mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord);
+typedef void AsyncDispatchFunc(mDNS *const m, void *context);
+typedef void DNSSECAuthInfoFreeCallback(mDNS *const m, void *context);
+extern void mDNSPlatformDispatchAsync(mDNS *const m, void *context, AsyncDispatchFunc func);
 
 #define NextQSendTime(Q)  ((Q)->LastQTime + (Q)->ThisQInterval)
 #define ActiveQuestion(Q) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf)
 
 #define NextQSendTime(Q)  ((Q)->LastQTime + (Q)->ThisQInterval)
 #define ActiveQuestion(Q) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf)
@@ -1621,7 +1856,33 @@ typedef void mDNSQuestionCallback (mDNS *const m, DNSQuestion *question, const R
 // questions from /etc/hosts, then we go straight to DNSSECValDone from the initial state.
 typedef enum { DNSSECValNotRequired = 0, DNSSECValRequired, DNSSECValInProgress, DNSSECValDone } DNSSECValState;
 
 // questions from /etc/hosts, then we go straight to DNSSECValDone from the initial state.
 typedef enum { DNSSECValNotRequired = 0, DNSSECValRequired, DNSSECValInProgress, DNSSECValDone } DNSSECValState;
 
-#define DNSSECQuestion(q) ((q)->ValidationRequired || (q)->ValidatingResponse)
+// ValidationRequired can be set to the following values:
+//
+// SECURE validation is set to determine whether something is secure or bogus 
+// INSECURE validation is set internally by dnssec code to indicate that it is currently proving something
+// is insecure
+#define DNSSEC_VALIDATION_NONE              0x00
+#define DNSSEC_VALIDATION_SECURE            0x01
+#define DNSSEC_VALIDATION_SECURE_OPTIONAL   0x02
+#define DNSSEC_VALIDATION_INSECURE          0x03
+
+// For both ValidationRequired and ValidatingResponse question, we validate DNSSEC responses.
+// For ProxyQuestion with DNSSECOK, we just receive the DNSSEC records to pass them along without
+// validation and if the CD bit is not set, we also validate.
+#define DNSSECQuestion(q) ((q)->ValidationRequired || (q)->ValidatingResponse || ((q)->ProxyQuestion && (q)->ProxyDNSSECOK))
+
+// ValidatingQuestion is used when we need to know whether we are validating the DNSSEC responses for a question
+#define ValidatingQuestion(q) ((q)->ValidationRequired || (q)->ValidatingResponse)
+
+#define DNSSECOptionalQuestion(q) ((q)->ValidationRequired == DNSSEC_VALIDATION_SECURE_OPTIONAL)
+
+// Given the resource record and the question, should we follow the CNAME ?
+#define FollowCNAME(q, rr, AddRecord)   (AddRecord && (q)->qtype != kDNSType_CNAME && \
+                                         (rr)->RecordType != kDNSRecordTypePacketNegative && \
+                                         (rr)->rrtype == kDNSType_CNAME)
+
+// RFC 4122 defines it to be 16 bytes 
+#define UUID_SIZE       16
 
 struct DNSQuestion_struct
 {
 
 struct DNSQuestion_struct
 {
@@ -1638,32 +1899,47 @@ struct DNSQuestion_struct
     mDNSs32 LastAnswerPktNum;               // The sequence number of the last response packet containing an answer to this Q
     mDNSu32 RecentAnswerPkts;               // Number of answers since the last time we sent this query
     mDNSu32 CurrentAnswers;                 // Number of records currently in the cache that answer this question
     mDNSs32 LastAnswerPktNum;               // The sequence number of the last response packet containing an answer to this Q
     mDNSu32 RecentAnswerPkts;               // Number of answers since the last time we sent this query
     mDNSu32 CurrentAnswers;                 // Number of records currently in the cache that answer this question
+    mDNSu32 BrowseThreshold;                // If we have received at least this number of answers,
+                                            // set the next question interval to MaxQuestionInterval
     mDNSu32 LargeAnswers;                   // Number of answers with rdata > 1024 bytes
     mDNSu32 UniqueAnswers;                  // Number of answers received with kDNSClass_UniqueRRSet bit set
     mDNSu32 LargeAnswers;                   // Number of answers with rdata > 1024 bytes
     mDNSu32 UniqueAnswers;                  // Number of answers received with kDNSClass_UniqueRRSet bit set
-    mDNSInterfaceID FlappingInterface1;      // Set when an interface goes away, to flag if remove events are delivered for this Q
-    mDNSInterfaceID FlappingInterface2;      // Set when an interface goes away, to flag if remove events are delivered for this Q
+    mDNSInterfaceID FlappingInterface1;     // Set when an interface goes away, to flag if remove events are delivered for this Q
+    mDNSInterfaceID FlappingInterface2;     // Set when an interface goes away, to flag if remove events are delivered for this Q
     DomainAuthInfo       *AuthInfo;         // Non-NULL if query is currently being done using Private DNS
     DNSQuestion          *DuplicateOf;
     DNSQuestion          *NextInDQList;
     DomainAuthInfo       *AuthInfo;         // Non-NULL if query is currently being done using Private DNS
     DNSQuestion          *DuplicateOf;
     DNSQuestion          *NextInDQList;
+    AnonymousInfo        *AnonInfo;         // Anonymous Information
     DupSuppressInfo DupSuppress[DupSuppressInfoSize];
     mDNSInterfaceID SendQNow;               // The interface this query is being sent on right now
     mDNSBool SendOnAll;                     // Set if we're sending this question on all active interfaces
     DupSuppressInfo DupSuppress[DupSuppressInfoSize];
     mDNSInterfaceID SendQNow;               // The interface this query is being sent on right now
     mDNSBool SendOnAll;                     // Set if we're sending this question on all active interfaces
+    mDNSBool CachedAnswerNeedsUpdate;       // See SendQueries().  Set if we're sending this question 
+                                            // because a cached answer needs to be refreshed.
     mDNSu32 RequestUnicast;                 // Non-zero if we want to send query with kDNSQClass_UnicastResponse bit set
     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
     mDNSu32 RequestUnicast;                 // Non-zero if we want to send query with kDNSQClass_UnicastResponse bit set
     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
+                                            // answering A, AAAA, CNAME, or PTR (/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
 
     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
+    // DNSSEC fields
+    DNSSECValState ValidationState;            // Current state of the Validation process
+    DNSSECStatus ValidationStatus;             // Validation status for "ValidationRequired" questions (dnssec)
+    mDNSu8 ValidatingResponse;                 // Question trying to validate a response (dnssec) on behalf of
+                                               // ValidationRequired question
+    void *DNSSECAuthInfo;
+    DNSSECAuthInfoFreeCallback *DAIFreeCallback;
+
+    // Wide Area fields. These are used internally by the uDNS core (Unicast)
     UDPSocket            *LocalSocket;
     UDPSocket            *LocalSocket;
+
+    // |-> DNS Configuration related fields used in uDNS (Subset of Wide Area/Unicast fields)
     DNSServer            *qDNSServer;       // Caching server for this query (in the absence of an SRV saying otherwise)
     mDNSOpaque64 validDNSServers;           // Valid DNSServers for this question
     mDNSu16 noServerResponse;               // At least one server did not respond.
     DNSServer            *qDNSServer;       // Caching server for this query (in the absence of an SRV saying otherwise)
     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
+    mDNSu16 triedAllServersOnce;            // Tried all DNS servers once
+    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
 
     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
@@ -1672,6 +1948,7 @@ struct DNSQuestion_struct
     mDNSIPPort tcpSrcPort;                  // Local Port TCP packet received on;need this as tcp struct is disposed
                                             // by tcpCallback before calling into mDNSCoreReceive
     mDNSu8 NoAnswer;                        // Set if we want to suppress answers until tunnel setup has completed
     mDNSIPPort tcpSrcPort;                  // Local Port TCP packet received on;need this as tcp struct is disposed
                                             // by tcpCallback before calling into mDNSCoreReceive
     mDNSu8 NoAnswer;                        // Set if we want to suppress answers until tunnel setup has completed
+    mDNSu8 Restart;                         // This question should be restarted soon
 
     // LLQ-specific fields. These fields are only meaningful when LongLived flag is set
     LLQ_State state;
 
     // LLQ-specific fields. These fields are only meaningful when LongLived flag is set
     LLQ_State state;
@@ -1681,12 +1958,14 @@ struct DNSQuestion_struct
                                             // for TCP: there is some ambiguity in the use of this variable, but in general, it is
                                             //          the number of TCP/TLS connection attempts for this LLQ state, or
                                             //          the number of packets sent for this TCP/TLS connection
                                             // for TCP: there is some ambiguity in the use of this variable, but in general, it is
                                             //          the number of TCP/TLS connection attempts for this LLQ state, or
                                             //          the number of packets sent for this TCP/TLS connection
-    DNSSECValState ValidationState;            // Current state of the Validation process
-    DNSSECStatus ValidationStatus;             // Validation status for "ValidationRequired" questions (dnssec)
-    mDNSu8 ValidatingResponse;                 // Question trying to validate a response (dnssec) on behalf of
-                                               // ValidationRequired question
     mDNSOpaque64 id;
 
     mDNSOpaque64 id;
 
+    // DNS Proxy fields
+    mDNSOpaque16 responseFlags;             // Temporary place holder for the error we get back from the DNS server
+                                            // till we populate in the cache
+    mDNSBool     DisallowPID;               // Is the query allowed for the "PID" that we are sending on behalf of ?
+    mDNSs32      ServiceID;                 // Service identifier to match against the DNS server
+    
     // Client API fields: The client must set up these fields *before* calling mDNS_StartQuery()
     mDNSInterfaceID InterfaceID;            // Non-zero if you want to issue queries only on a single specific IP interface
     mDNSu32  flags;                         // flags from original DNSService*() API request.
     // Client API fields: The client must set up these fields *before* calling mDNS_StartQuery()
     mDNSInterfaceID InterfaceID;            // Non-zero if you want to issue queries only on a single specific IP interface
     mDNSu32  flags;                         // flags from original DNSService*() API request.
@@ -1701,15 +1980,19 @@ 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 ForceMCast;                    // Set by client to force mDNS query, even for apparently uDNS names
     mDNSBool ReturnIntermed;                // Set by client to request callbacks for intermediate CNAME/NXDOMAIN results
     mDNSBool SuppressUnusable;              // Set by client to suppress unusable queries to be sent on the wire
-    mDNSBool RetryWithSearchDomains;                // Retry with search domains if there is no entry in the cache or AuthRecords
+    mDNSu8 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
     mDNSu8 TimeoutQuestion;                 // Timeout this question if there is no reply in configured time
     mDNSu8 WakeOnResolve;                   // Send wakeup on resolve
-    mDNSu8 UseBrackgroundTrafficClass;      // Use background traffic class for request
+    mDNSu8 UseBackgroundTrafficClass;       // Use background traffic class for request
     mDNSs8 SearchListIndex;                 // Index into SearchList; Used by the client layer but not touched by core
     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
-    mDNSu8 ValidationRequired;                      // Requires DNSSEC validation.
-    domainname           *qnameOrig;       // Copy of the original question name if it is not fully qualified
+    mDNSs8 AppendSearchDomains;             // Search domains can be appended for this query
+    mDNSs8 AppendLocalSearchDomains;        // Search domains ending in .local can be appended for this query
+    mDNSu8 ValidationRequired;              // Requires DNSSEC validation.
+    mDNSu8 ProxyQuestion;                   // Proxy Question
+    mDNSu8 ProxyDNSSECOK;                   // Proxy Question with EDNS0 DNSSEC OK bit set
+    mDNSs32 pid;                            // Process ID of the client that is requesting the question
+    mDNSu8  uuid[UUID_SIZE];                // Unique ID of the client that is requesting the question (valid only if pid is zero)
+    domainname           *qnameOrig;        // Copy of the original question name if it is not fully qualified
     mDNSQuestionCallback *QuestionCallback;
     void                 *QuestionContext;
 };
     mDNSQuestionCallback *QuestionCallback;
     void                 *QuestionContext;
 };
@@ -1853,10 +2136,14 @@ struct NetworkInterfaceInfo_struct
     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
     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
+    mDNSu8 IgnoreIPv4LL;                // Set if IPv4 Link-Local addresses have to be ignored.
+    mDNSu8 SendGoodbyes;                // Send goodbyes on this interface while sleeping
 };
 
 };
 
-#define SLE_DELETE              0x00000001
-#define SLE_WAB_QUERY_STARTED   0x00000002
+#define SLE_DELETE                      0x00000001
+#define SLE_WAB_BROWSE_QUERY_STARTED    0x00000002
+#define SLE_WAB_LBROWSE_QUERY_STARTED   0x00000004
+#define SLE_WAB_REG_QUERY_STARTED       0x00000008
 
 typedef struct SearchListElem
 {
 
 typedef struct SearchListElem
 {
@@ -1888,13 +2175,9 @@ extern SearchListElem *SearchList;      // This really ought to be part of mDNS_
 
 typedef void mDNSCallback (mDNS *const m, mStatus result);
 
 
 typedef void mDNSCallback (mDNS *const m, mStatus result);
 
+#ifndef CACHE_HASH_SLOTS
 #define CACHE_HASH_SLOTS 499
 #define CACHE_HASH_SLOTS 499
-
-enum        // Bit flags -- i.e. values should be 1, 2, 4, 8, etc.
-{
-    mDNS_KnownBug_LimitedIPv6       = 1,
-    mDNS_KnownBug_LossySyslog       = 2     // <rdar://problem/6561888>
-};
+#endif
 
 enum
 {
 
 enum
 {
@@ -1903,6 +2186,74 @@ enum
     SleepState_Sleeping = 2
 };
 
     SleepState_Sleeping = 2
 };
 
+typedef enum
+{
+    kStatsActionIncrement,
+    kStatsActionDecrement,
+    kStatsActionClear,
+    kStatsActionSet
+} DNSSECStatsAction;
+
+typedef enum
+{
+    kStatsTypeMemoryUsage,
+    kStatsTypeLatency,
+    kStatsTypeExtraPackets,
+    kStatsTypeStatus,
+    kStatsTypeProbe,
+    kStatsTypeMsgSize
+} DNSSECStatsType;
+
+typedef struct
+{
+    mDNSu32 TotalMemUsed;
+    mDNSu32 Latency0;           // 0 to 4 ms
+    mDNSu32 Latency5;           // 5 to  9 ms
+    mDNSu32 Latency10;          // 10 to 19 ms
+    mDNSu32 Latency20;          // 20 to 49 ms
+    mDNSu32 Latency50;          // 50 to 99 ms
+    mDNSu32 Latency100;         // >= 100 ms
+    mDNSu32 ExtraPackets0;      // 0 to 2 packets
+    mDNSu32 ExtraPackets3;      // 3 to 6 packets
+    mDNSu32 ExtraPackets7;      // 7 to 9 packets
+    mDNSu32 ExtraPackets10;     // >= 10 packets
+    mDNSu32 SecureStatus;
+    mDNSu32 InsecureStatus;
+    mDNSu32 IndeterminateStatus;
+    mDNSu32 BogusStatus;
+    mDNSu32 NoResponseStatus;
+    mDNSu32 NumProbesSent;      // Number of probes sent
+    mDNSu32 MsgSize0;           // DNSSEC message size <= 1024
+    mDNSu32 MsgSize1;           // DNSSEC message size <= 2048
+    mDNSu32 MsgSize2;           // DNSSEC message size > 2048
+} DNSSECStatistics;
+
+typedef struct
+{
+    mDNSu32 NameConflicts;                  // Normal Name conflicts
+    mDNSu32 KnownUniqueNameConflicts;       // Name Conflicts for KnownUnique Records
+    mDNSu32 DupQuerySuppressions;           // Duplicate query suppressions
+    mDNSu32 KnownAnswerSuppressions;        // Known Answer suppressions
+    mDNSu32 KnownAnswerMultiplePkts;        // Known Answer in queries spannign multiple packets
+    mDNSu32 PoofCacheDeletions;             // Number of times the cache was deleted due to POOF
+    mDNSu32 UnicastBitInQueries;            // Queries with QU bit set
+    mDNSu32 NormalQueries;                  // Queries with QU bit not set
+    mDNSu32 MatchingAnswersForQueries;      // Queries for which we had a response
+    mDNSu32 UnicastResponses;               // Unicast responses to queries
+    mDNSu32 MulticastResponses;             // Multicast responses to queries
+    mDNSu32 UnicastDemotedToMulticast;      // Number of times unicast demoted to multicast
+    mDNSu32 Sleeps;                         // Total sleeps
+    mDNSu32 Wakes;                          // Total wakes
+    mDNSu32 InterfaceUp;                    // Total Interface UP events
+    mDNSu32 InterfaceUpFlap;                // Total Interface UP events with flaps
+    mDNSu32 InterfaceDown;                  // Total Interface Down events
+    mDNSu32 InterfaceDownFlap;              // Total Interface Down events with flaps
+    mDNSu32 CacheRefreshQueries;            // Number of queries that we sent for refreshing cache
+    mDNSu32 CacheRefreshed;                 // Number of times the cache was refreshed due to a response
+    mDNSu32 WakeOnResolves;                 // Number of times we did a wake on resolve
+} mDNSStatistics;
+extern void LogMDNSStatistics(mDNS *const m);
+
 struct mDNS_struct
 {
     // Internal state fields. These hold the main internal state of mDNSCore;
 struct mDNS_struct
 {
     // Internal state fields. These hold the main internal state of mDNSCore;
@@ -1911,7 +2262,6 @@ struct mDNS_struct
     // all required data is passed as parameters to that function.
 
     mDNS_PlatformSupport *p;            // Pointer to platform-specific data of indeterminite size
     // all required data is passed as parameters to that function.
 
     mDNS_PlatformSupport *p;            // Pointer to platform-specific data of indeterminite size
-    mDNSu32 KnownBugs;
     mDNSBool CanReceiveUnicastOn5353;
     mDNSBool AdvertiseLocalAddresses;
     mDNSBool DivertMulticastAdvertisements; // from interfaces that do not advertise local addresses to local-only
     mDNSBool CanReceiveUnicastOn5353;
     mDNSBool AdvertiseLocalAddresses;
     mDNSBool DivertMulticastAdvertisements; // from interfaces that do not advertise local addresses to local-only
@@ -1950,6 +2300,7 @@ struct mDNS_struct
     mDNSs32 RandomQueryDelay;           // For de-synchronization of query packets on the wire
     mDNSu32 RandomReconfirmDelay;       // For de-synchronization of reconfirmation queries on the wire
     mDNSs32 PktNum;                     // Unique sequence number assigned to each received packet
     mDNSs32 RandomQueryDelay;           // For de-synchronization of query packets on the wire
     mDNSu32 RandomReconfirmDelay;       // For de-synchronization of reconfirmation queries on the wire
     mDNSs32 PktNum;                     // Unique sequence number assigned to each received packet
+    mDNSs32 MPktNum;                    // Unique sequence number assigned to each received Multicast packet
     mDNSu8 LocalRemoveEvents;           // Set if we may need to deliver remove events for local-only questions and/or local-only records
     mDNSu8 SleepState;                  // Set if we're sleeping
     mDNSu8 SleepSeqNum;                 // "Epoch number" of our current period of wakefulness
     mDNSu8 LocalRemoveEvents;           // Set if we may need to deliver remove events for local-only questions and/or local-only records
     mDNSu8 SleepState;                  // Set if we're sleeping
     mDNSu8 SleepSeqNum;                 // "Epoch number" of our current period of wakefulness
@@ -1957,16 +2308,24 @@ struct mDNS_struct
     mDNSu8 SentSleepProxyRegistration;  // Set if we registered (or tried to register) with a Sleep Proxy
     mDNSu8 SystemSleepOnlyIfWakeOnLAN;  // Set if we may only sleep if we managed to register with a Sleep Proxy
     mDNSs32 AnnounceOwner;              // After waking from sleep, include OWNER option in packets until this time
     mDNSu8 SentSleepProxyRegistration;  // Set if we registered (or tried to register) with a Sleep Proxy
     mDNSu8 SystemSleepOnlyIfWakeOnLAN;  // Set if we may only sleep if we managed to register with a Sleep Proxy
     mDNSs32 AnnounceOwner;              // After waking from sleep, include OWNER option in packets until this time
-    mDNSs32 clearIgnoreNA;              // After waking from sleep, clear the ignore neighbor advertisement after this time
     mDNSs32 DelaySleep;                 // To inhibit re-sleeping too quickly right after wake
     mDNSs32 SleepLimit;                 // Time window to allow deregistrations, etc.,
                                         // during which underying platform layer should inhibit system sleep
     mDNSs32 DelaySleep;                 // To inhibit re-sleeping too quickly right after wake
     mDNSs32 SleepLimit;                 // Time window to allow deregistrations, etc.,
                                         // during which underying platform layer should inhibit system sleep
+    mDNSs32 TimeSlept;                  // Time we went to sleep.
+
+    mDNSs32 StatStartTime;              // Time we started gathering statistics during this interval.
+    mDNSs32 NextStatLogTime;            // Next time to log statistics.
+    mDNSs32 ActiveStatTime;             // Total time awake/gathering statistics for this log period.
+    mDNSs32 UnicastPacketsSent;         // Number of unicast packets sent.
+    mDNSs32 MulticastPacketsSent;       // Number of multicast packets sent.
+    mDNSs32 RemoteSubnet;               // Multicast packets received from outside our subnet.
+
     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
     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
-    mDNSs32 ClearSPSRecords;            // Time to clear stored Addr/AAAA records that were registered with a Sleep Proxy
+
+
     // 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
     // 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
@@ -1977,6 +2336,7 @@ struct mDNS_struct
     DNSQuestion *ValidationQuestion;    // Questions that are being validated (dnssec)
     mDNSu32 rrcache_size;               // Total number of available cache entries
     mDNSu32 rrcache_totalused;          // Number of cache entries currently occupied
     DNSQuestion *ValidationQuestion;    // Questions that are being validated (dnssec)
     mDNSu32 rrcache_size;               // Total number of available cache entries
     mDNSu32 rrcache_totalused;          // Number of cache entries currently occupied
+    mDNSu32 rrcache_totalused_unicast;  // Number of cache entries currently occupied by unicast
     mDNSu32 rrcache_active;             // Number of cache entries currently occupied by records that answer active questions
     mDNSu32 rrcache_report;
     CacheEntity *rrcache_free;
     mDNSu32 rrcache_active;             // Number of cache entries currently occupied by records that answer active questions
     mDNSu32 rrcache_report;
     CacheEntity *rrcache_free;
@@ -2001,6 +2361,7 @@ struct mDNS_struct
     mDNSs32 ProbeFailTime;
     mDNSu32 NumFailedProbes;
     mDNSs32 SuppressProbes;
     mDNSs32 ProbeFailTime;
     mDNSu32 NumFailedProbes;
     mDNSs32 SuppressProbes;
+    Platform_t mDNS_plat;
 
     // Unicast-specific data
     mDNSs32 NextuDNSEvent;                  // uDNS next event
 
     // Unicast-specific data
     mDNSs32 NextuDNSEvent;                  // uDNS next event
@@ -2023,18 +2384,21 @@ struct mDNS_struct
     NATTraversalInfo AutoTunnelNAT;         // Shared between all AutoTunnel DomainAuthInfo structs
     mDNSv6Addr AutoTunnelRelayAddr;
 
     NATTraversalInfo AutoTunnelNAT;         // Shared between all AutoTunnel DomainAuthInfo structs
     mDNSv6Addr AutoTunnelRelayAddr;
 
-    mDNSBool StartWABQueries;               // Start WAB queries for the purpose of domain enumeration
+    mDNSu32 WABBrowseQueriesCount;          // Number of WAB Browse domain enumeration queries (b, db) callers
+    mDNSu32 WABLBrowseQueriesCount;         // Number of legacy WAB Browse domain enumeration queries (lb) callers
+    mDNSu32 WABRegQueriesCount;             // Number of WAB Registration domain enumeration queries (r, dr) callers
     mDNSu8 SearchDomainsHash[MD5_LEN];
 
     // NAT-Traversal fields
     NATTraversalInfo LLQNAT;                    // Single shared NAT Traversal to receive inbound LLQ notifications
     NATTraversalInfo *NATTraversals;
     NATTraversalInfo *CurrentNATTraversal;
     mDNSu8 SearchDomainsHash[MD5_LEN];
 
     // NAT-Traversal fields
     NATTraversalInfo LLQNAT;                    // Single shared NAT Traversal to receive inbound LLQ notifications
     NATTraversalInfo *NATTraversals;
     NATTraversalInfo *CurrentNATTraversal;
-    mDNSs32 retryIntervalGetAddr;               // delta between time sent and retry
-    mDNSs32 retryGetAddr;                       // absolute time when we retry
-    mDNSv4Addr ExternalAddress;
+    mDNSs32 retryIntervalGetAddr;               // delta between time sent and retry for NAT-PMP & UPnP/IGD external address request
+    mDNSs32 retryGetAddr;                       // absolute time when we retry for NAT-PMP & UPnP/IGD external address request
+    mDNSv4Addr ExtAddress;                      // the external address discovered via NAT-PMP or UPnP/IGD
+    mDNSu32 PCPNonce[3];                        // the nonce if using PCP
 
 
-    UDPSocket        *NATMcastRecvskt;          // For receiving NAT-PMP AddrReply multicasts from router on port 5350
+    UDPSocket        *NATMcastRecvskt;          // For receiving PCP & NAT-PMP announcement multicasts from router on port 5350
     mDNSu32 LastNATupseconds;                   // NAT engine uptime in seconds, from most recent NAT packet
     mDNSs32 LastNATReplyLocalTime;              // Local time in ticks when most recent NAT packet was received
     mDNSu16 LastNATMapResultCode;               // Most recent error code for mappings
     mDNSu32 LastNATupseconds;                   // NAT engine uptime in seconds, from most recent NAT packet
     mDNSs32 LastNATReplyLocalTime;              // Local time in ticks when most recent NAT packet was received
     mDNSu16 LastNATMapResultCode;               // Most recent error code for mappings
@@ -2054,7 +2418,7 @@ struct mDNS_struct
     mDNSu8           *UPnPSOAPAddressString;    // holds both address and port for SOAP messages
 
     // Sleep Proxy client fields
     mDNSu8           *UPnPSOAPAddressString;    // holds both address and port for SOAP messages
 
     // Sleep Proxy client fields
-    AuthRecord *SPSRRSet;               // To help the client keep track of the records registered with the sleep proxy
+    AuthRecord *SPSRRSet;                       // To help the client keep track of the records registered with the sleep proxy
 
     // Sleep Proxy Server fields
     mDNSu8 SPSType;                             // 0 = off, 10-99 encodes desirability metric
 
     // Sleep Proxy Server fields
     mDNSu8 SPSType;                             // 0 = off, 10-99 encodes desirability metric
@@ -2065,7 +2429,9 @@ struct mDNS_struct
     mDNSu8 SPSState;                            // 0 = off, 1 = running, 2 = shutting down, 3 = suspended during sleep
     mDNSInterfaceID SPSProxyListChanged;
     UDPSocket        *SPSSocket;
     mDNSu8 SPSState;                            // 0 = off, 1 = running, 2 = shutting down, 3 = suspended during sleep
     mDNSInterfaceID SPSProxyListChanged;
     UDPSocket        *SPSSocket;
+#ifndef SPC_DISABLED
     ServiceRecordSet SPSRecords;
     ServiceRecordSet SPSRecords;
+#endif
     mDNSQuestionCallback *SPSBrowseCallback;    // So the platform layer can do something useful with SPS browse results
     int ProxyRecords;                           // Total number of records we're holding as proxy
     #define           MAX_PROXY_RECORDS 10000   /* DOS protection: 400 machines at 25 records each */
     mDNSQuestionCallback *SPSBrowseCallback;    // So the platform layer can do something useful with SPS browse results
     int ProxyRecords;                           // Total number of records we're holding as proxy
     #define           MAX_PROXY_RECORDS 10000   /* DOS protection: 400 machines at 25 records each */
@@ -2075,9 +2441,17 @@ struct mDNS_struct
     uuid_t asl_uuid;                            // uuid for ASL logging
     void            *WCF;
 #endif
     uuid_t asl_uuid;                            // uuid for ASL logging
     void            *WCF;
 #endif
+    // DNS Proxy fields
+    mDNSu32 dp_ipintf[MaxIp];                   // input interface index list from the DNS Proxy Client
+    mDNSu32 dp_opintf;                          // output interface index from the DNS Proxy Client
+
     TrustAnchor     *TrustAnchors;
     int             notifyToken;
     TrustAnchor     *TrustAnchors;
     int             notifyToken;
-    mDNSBool        mDNSHandlePeerEvents;       // Handle AWDL Peer Events
+    int             uds_listener_skt;           // Listening socket for incoming UDS clients
+    mDNSBool        mDNSOppCaching;             // Opportunistic Caching
+    mDNSu32         AutoTargetServices;         // # of services that have AutoTarget set
+    DNSSECStatistics DNSSECStats;
+    mDNSStatistics   mDNSStats;
 
     // Fixed storage, to avoid creating large objects on the stack
     // The imsg is declared as a union with a pointer type to enforce CPU-appropriate alignment
 
     // Fixed storage, to avoid creating large objects on the stack
     // The imsg is declared as a union with a pointer type to enforce CPU-appropriate alignment
@@ -2102,6 +2476,7 @@ extern const mDNSInterfaceID mDNSInterface_LocalOnly;       // Special value
 extern const mDNSInterfaceID mDNSInterface_Unicast;         // Special value
 extern const mDNSInterfaceID mDNSInterfaceMark;             // Special value
 extern const mDNSInterfaceID mDNSInterface_P2P;             // Special value
 extern const mDNSInterfaceID mDNSInterface_Unicast;         // Special value
 extern const mDNSInterfaceID mDNSInterfaceMark;             // Special value
 extern const mDNSInterfaceID mDNSInterface_P2P;             // Special value
+extern const mDNSInterfaceID uDNSInterfaceMark;             // Special value
 
 extern const mDNSIPPort DiscardPort;
 extern const mDNSIPPort SSHPort;
 
 extern const mDNSIPPort DiscardPort;
 extern const mDNSIPPort SSHPort;
@@ -2151,6 +2526,7 @@ extern mDNSu8 NumUnicastDNSServers;
 
 #define localdomain           (*(const domainname *)"\x5" "local")
 #define DeviceInfoName        (*(const domainname *)"\xC" "_device-info" "\x4" "_tcp")
 
 #define localdomain           (*(const domainname *)"\x5" "local")
 #define DeviceInfoName        (*(const domainname *)"\xC" "_device-info" "\x4" "_tcp")
+#define LocalDeviceInfoName   (*(const domainname *)"\xC" "_device-info" "\x4" "_tcp" "\x5" "local")
 #define SleepProxyServiceType (*(const domainname *)"\xC" "_sleep-proxy" "\x4" "_udp")
 
 // ***************************************************************************
 #define SleepProxyServiceType (*(const domainname *)"\xC" "_sleep-proxy" "\x4" "_udp")
 
 // ***************************************************************************
@@ -2282,6 +2658,7 @@ extern mStatus mDNS_StartQuery(mDNS *const m, DNSQuestion *const question);
 extern mStatus mDNS_StopQuery (mDNS *const m, DNSQuestion *const question);
 extern mStatus mDNS_StopQueryWithRemoves(mDNS *const m, DNSQuestion *const question);
 extern mStatus mDNS_Reconfirm (mDNS *const m, CacheRecord *const cacherr);
 extern mStatus mDNS_StopQuery (mDNS *const m, DNSQuestion *const question);
 extern mStatus mDNS_StopQueryWithRemoves(mDNS *const m, DNSQuestion *const question);
 extern mStatus mDNS_Reconfirm (mDNS *const m, CacheRecord *const cacherr);
+extern mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, mDNSu32 interval);
 extern mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr);
 extern void    mDNS_PurgeCacheResourceRecord(mDNS *const m, CacheRecord *rr);
 extern mDNSs32 mDNS_TimeNow(const mDNS *const m);
 extern mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr);
 extern void    mDNS_PurgeCacheResourceRecord(mDNS *const m, CacheRecord *rr);
 extern mDNSs32 mDNS_TimeNow(const mDNS *const m);
@@ -2346,7 +2723,8 @@ enum
 {
     coreFlagIncludeP2P   = 0x1,     // include P2P interfaces when using mDNSInterface_Any
     coreFlagIncludeAWDL  = 0x2,     // include AWDL interface when using mDNSInterface_Any
 {
     coreFlagIncludeP2P   = 0x1,     // include P2P interfaces when using mDNSInterface_Any
     coreFlagIncludeAWDL  = 0x2,     // include AWDL interface when using mDNSInterface_Any
-    coreFlagKnownUnique  = 0x4      // client guarantees that SRV and TXT record names are unique
+    coreFlagKnownUnique  = 0x4,     // client guarantees that SRV and TXT record names are unique
+    coreFlagWakeOnly     = 0x8      // Service won't be registered with sleep proxy
 };
 
 extern mStatus mDNS_RegisterService  (mDNS *const m, ServiceRecordSet *sr,
 };
 
 extern mStatus mDNS_RegisterService  (mDNS *const m, ServiceRecordSet *sr,
@@ -2370,7 +2748,7 @@ extern void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID Inter
                                const mDNSu16 qtype, mDNSQuestionCallback *const callback, void *const context);
 
 extern mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question,
                                const mDNSu16 qtype, mDNSQuestionCallback *const callback, void *const context);
 
 extern mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question,
-                                const domainname *const srv, const domainname *const domain,
+                                const domainname *const srv, const domainname *const domain, const mDNSu8 *anondata,
                                 const mDNSInterfaceID InterfaceID, mDNSu32 flags,
                                 mDNSBool ForceMCast, mDNSBool useBackgroundTrafficClass,
                                 mDNSQuestionCallback *Callback, void *Context);
                                 const mDNSInterfaceID InterfaceID, mDNSu32 flags,
                                 mDNSBool ForceMCast, mDNSBool useBackgroundTrafficClass,
                                 mDNSQuestionCallback *Callback, void *Context);
@@ -2399,9 +2777,8 @@ extern mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, mDNS_DomainT
 #define        mDNS_StopAdvertiseDomains mDNS_Deregister
 
 extern mDNSOpaque16 mDNS_NewMessageID(mDNS *const m);
 #define        mDNS_StopAdvertiseDomains mDNS_Deregister
 
 extern mDNSOpaque16 mDNS_NewMessageID(mDNS *const m);
-extern mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr);
+extern mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr, mDNSBool *myself);
 
 
-extern DNSServer *GetServerForName(mDNS *m, const domainname *name, mDNSInterfaceID InterfaceID);
 extern DNSServer *GetServerForQuestion(mDNS *m, DNSQuestion *question);
 extern mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question);
 
 extern DNSServer *GetServerForQuestion(mDNS *m, DNSQuestion *question);
 extern mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question);
 
@@ -2513,9 +2890,13 @@ extern char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RDataB
 #define CRDisplayString(m, rr) GetRRDisplayString_rdb(&(rr)->resrec, &(rr)->resrec.rdata->u, (m)->MsgBuffer)
 extern mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2);
 extern void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText);
 #define CRDisplayString(m, rr) GetRRDisplayString_rdb(&(rr)->resrec, &(rr)->resrec.rdata->u, (m)->MsgBuffer)
 extern mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2);
 extern void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText);
-extern mDNSBool mDNSv4AddrIsRFC1918(mDNSv4Addr *addr);  // returns true for RFC1918 private addresses
+extern mDNSBool mDNSv4AddrIsRFC1918(const mDNSv4Addr * const addr);  // returns true for RFC1918 private addresses
 #define mDNSAddrIsRFC1918(X) ((X)->type == mDNSAddrType_IPv4 && mDNSv4AddrIsRFC1918(&(X)->ip.v4))
 
 #define mDNSAddrIsRFC1918(X) ((X)->type == mDNSAddrType_IPv4 && mDNSv4AddrIsRFC1918(&(X)->ip.v4))
 
+// For PCP
+extern void mDNSAddrMapIPv4toIPv6(mDNSv4Addr* in, mDNSv6Addr* out);
+extern mDNSBool mDNSAddrIPv4FromMappedIPv6(mDNSv6Addr *in, mDNSv4Addr *out);
+
 #define mDNSSameIPPort(A,B)      ((A).NotAnInteger == (B).NotAnInteger)
 #define mDNSSameOpaque16(A,B)    ((A).NotAnInteger == (B).NotAnInteger)
 #define mDNSSameOpaque32(A,B)    ((A).NotAnInteger == (B).NotAnInteger)
 #define mDNSSameIPPort(A,B)      ((A).NotAnInteger == (B).NotAnInteger)
 #define mDNSSameOpaque16(A,B)    ((A).NotAnInteger == (B).NotAnInteger)
 #define mDNSSameOpaque32(A,B)    ((A).NotAnInteger == (B).NotAnInteger)
@@ -2589,7 +2970,7 @@ extern mDNSBool mDNSv4AddrIsRFC1918(mDNSv4Addr *addr);  // returns true for RFC1
 extern mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info,
                                        const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, mDNSBool autoTunnel);
 
 extern mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info,
                                        const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, mDNSBool autoTunnel);
 
-extern void RecreateNATMappings(mDNS *const m);
+extern void RecreateNATMappings(mDNS *const m, const mDNSu32 waitTicks);
 
 // Hostname/Unicast Interface Configuration
 
 
 // Hostname/Unicast Interface Configuration
 
@@ -2611,8 +2992,10 @@ 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 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, mDNSu32 timeout, mDNSBool cellIntf, mDNSu16 resGroupID);
-extern void PenalizeDNSServer(mDNS *const m, DNSQuestion *q);
+extern DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, mDNSs32 serviceID, const mDNSAddr *addr,
+                                    const mDNSIPPort port, mDNSu32 scoped, mDNSu32 timeout, mDNSBool cellIntf, mDNSu16 resGroupID, mDNSBool reqA,
+                                    mDNSBool reqAAAA, mDNSBool reqDO);
+extern void PenalizeDNSServer(mDNS *const m, DNSQuestion *q, mDNSOpaque16 responseFlags);
 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);
 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);
@@ -2689,6 +3072,7 @@ extern mStatus  mDNSPlatformSendUDP(const mDNS *const m, const void *const msg,
                                     mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, 
                                     mDNSIPPort dstport, mDNSBool useBackgroundTrafficClass);
 
                                     mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, 
                                     mDNSIPPort dstport, mDNSBool useBackgroundTrafficClass);
 
+extern mDNSBool mDNSPlatformPeekUDP     (mDNS *const m, UDPSocket *src);
 extern void     mDNSPlatformLock        (const mDNS *const m);
 extern void     mDNSPlatformUnlock      (const mDNS *const m);
 
 extern void     mDNSPlatformLock        (const mDNS *const m);
 extern void     mDNSPlatformUnlock      (const mDNS *const m);
 
@@ -2732,9 +3116,12 @@ extern void mDNSPlatformWriteLogMsg(const char *ident, const char *msg, mDNSLogL
 // Utility function for ASL logging
 mDNSexport void mDNSASLLog(uuid_t *uuid, const char *subdomain, const char *result, const char *signature, const char *fmt, ...);
 
 // Utility function for ASL logging
 mDNSexport void mDNSASLLog(uuid_t *uuid, const char *subdomain, const char *result, const char *signature, const char *fmt, ...);
 
-// Function to toggle IPv6 advertisements
-mDNSexport void mDNSPlatformToggleInterfaceAdvt(mDNS *const m, mDNSBool stopAdvt);
-#endif
+// Log unicast and multicast traffic statistics once a day. Also used for DNSSEC statistics.
+#define kDefaultNextStatsticsLogTime (24 * 60 * 60)
+
+extern void mDNSLogStatistics(mDNS *const m);
+
+#endif // APPLE_OSX_mDNSResponder
 
 // Platform support modules should provide the following functions to map between opaque interface IDs
 // and interface indexes in order to support the DNS-SD API. If your target platform does not support
 
 // Platform support modules should provide the following functions to map between opaque interface IDs
 // and interface indexes in order to support the DNS-SD API. If your target platform does not support
@@ -2775,6 +3162,7 @@ extern void       mDNSPlatformTCPCloseConnection(TCPSocket *sock);
 extern long       mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long buflen, mDNSBool *closed);
 extern long       mDNSPlatformWriteTCP(TCPSocket *sock, const char *msg, unsigned long len);
 extern UDPSocket *mDNSPlatformUDPSocket(mDNS *const m, const mDNSIPPort requestedport);
 extern long       mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long buflen, mDNSBool *closed);
 extern long       mDNSPlatformWriteTCP(TCPSocket *sock, const char *msg, unsigned long len);
 extern UDPSocket *mDNSPlatformUDPSocket(mDNS *const m, const mDNSIPPort requestedport);
+extern mDNSu16    mDNSPlatformGetUDPPort(UDPSocket *sock);
 extern void       mDNSPlatformUDPClose(UDPSocket *sock);
 extern void       mDNSPlatformReceiveBPF_fd(mDNS *const m, int fd);
 extern void       mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID InterfaceID);
 extern void       mDNSPlatformUDPClose(UDPSocket *sock);
 extern void       mDNSPlatformReceiveBPF_fd(mDNS *const m, int fd);
 extern void       mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID InterfaceID);
@@ -2782,7 +3170,9 @@ extern void       mDNSPlatformSendRawPacket(const void *const msg, const mDNSu8
 extern void       mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSAddr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID);
 extern void       mDNSPlatformSourceAddrForDest(mDNSAddr *const src, const mDNSAddr *const dst);
 extern void       mDNSPlatformSendKeepalive(mDNSAddr *sadd, mDNSAddr *dadd, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu32 seq, mDNSu32 ack, mDNSu16 win);
 extern void       mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSAddr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID);
 extern void       mDNSPlatformSourceAddrForDest(mDNSAddr *const src, const mDNSAddr *const dst);
 extern void       mDNSPlatformSendKeepalive(mDNSAddr *sadd, mDNSAddr *dadd, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu32 seq, mDNSu32 ack, mDNSu16 win);
-extern mStatus    mDNSPlatformRetrieveTCPInfo(mDNS *const m, mDNSAddr *laddr, mDNSIPPort *lport, mDNSAddr *raddr, mDNSIPPort *rport, mDNSTCPInfo *mti);
+extern mStatus    mDNSPlatformRetrieveTCPInfo(mDNS *const m, mDNSAddr *laddr, mDNSIPPort *lport, mDNSAddr *raddr,  mDNSIPPort *rport, mDNSTCPInfo *mti);
+extern mStatus    mDNSPlatformGetRemoteMacAddr(mDNS *const m, mDNSAddr *raddr);
+extern mStatus    mDNSPlatformStoreSPSMACAddr(mDNSAddr *spsaddr, char *ifname);
 
 // mDNSPlatformTLSSetupCerts/mDNSPlatformTLSTearDownCerts used by dnsextd
 extern mStatus    mDNSPlatformTLSSetupCerts(void);
 
 // mDNSPlatformTLSSetupCerts/mDNSPlatformTLSTearDownCerts used by dnsextd
 extern mStatus    mDNSPlatformTLSSetupCerts(void);
@@ -2791,7 +3181,8 @@ extern void       mDNSPlatformTLSTearDownCerts(void);
 // Platforms that support unicast browsing and dynamic update registration for clients who do not specify a domain
 // in browse/registration calls must implement these routines to get the "default" browse/registration list.
 
 // Platforms that support unicast browsing and dynamic update registration for clients who do not specify a domain
 // in browse/registration calls must implement these routines to get the "default" browse/registration list.
 
-extern void       mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **RegDomains, DNameListElem **BrowseDomains);
+extern mDNSBool   mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **RegDomains,
+                        DNameListElem **BrowseDomains, mDNSBool ackConfig);
 extern mStatus    mDNSPlatformGetPrimaryInterface(mDNS *const m, mDNSAddr *v4, mDNSAddr *v6, mDNSAddr *router);
 extern void       mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const dname, const mStatus status);
 
 extern mStatus    mDNSPlatformGetPrimaryInterface(mDNS *const m, mDNSAddr *v4, mDNSAddr *v6, mDNSAddr *router);
 extern void       mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const dname, const mStatus status);
 
@@ -2894,6 +3285,9 @@ extern void ActivateUnicastRegistration(mDNS *const m, AuthRecord *const rr);
 extern void CheckSuppressUnusableQuestions(mDNS *const m);
 extern void RetrySearchDomainQuestions(mDNS *const m);
 extern mDNSBool DomainEnumQuery(const domainname *qname);
 extern void CheckSuppressUnusableQuestions(mDNS *const m);
 extern void RetrySearchDomainQuestions(mDNS *const m);
 extern mDNSBool DomainEnumQuery(const domainname *qname);
+extern mStatus UpdateKeepaliveRData(mDNS *const m, AuthRecord *rr, NetworkInterfaceInfo *const intf, mDNSBool updateMac, char *ethAddr);
+extern void  UpdateKeepaliveRMACAsync(mDNS *const m, void *context);
+extern void UpdateRMACCallback(mDNS *const m, void *context);
 
 // Used only in logging to restrict the number of /etc/hosts entries printed
 extern void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result);
 
 // Used only in logging to restrict the number of /etc/hosts entries printed
 extern void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result);
@@ -2913,14 +3307,32 @@ extern void StartServerTunnel(mDNS *const m, DomainAuthInfo *const info);
 extern void UpdateAutoTunnelDomainStatuses(const mDNS *const m);
 extern void RemoveAutoTunnel6Record(mDNS *const m);
 extern mDNSBool RecordReadyForSleep(mDNS *const m, AuthRecord *rr);
 extern void UpdateAutoTunnelDomainStatuses(const mDNS *const m);
 extern void RemoveAutoTunnel6Record(mDNS *const m);
 extern mDNSBool RecordReadyForSleep(mDNS *const m, AuthRecord *rr);
-#endif
-
 // For now this LocalSleepProxy 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
 // For now this LocalSleepProxy stuff is specific to Mac OS X.
 // In the future, if there's demand, we may see if we can abstract it out cleanly into the platform layer
+extern mStatus ActivateLocalProxy(mDNS *const m, NetworkInterfaceInfo *const intf);
+extern void mDNSPlatformUpdateDNSStatus(mDNS *const m, DNSQuestion *q);
+extern void mDNSPlatformTriggerDNSRetry(mDNS *const m, DNSQuestion *v4q, DNSQuestion *v6q);
+extern void mDNSPlatformLogToFile(int log_level, const char *buffer);
+extern mDNSBool SupportsInNICProxy(NetworkInterfaceInfo *const intf);
+#endif
+
+typedef void ProxyCallback (mDNS *const m, void *socket, void *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr,
+    const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context);
+extern void mDNSPlatformInitDNSProxySkts(mDNS *const m, ProxyCallback *UDPCallback, ProxyCallback *TCPCallback);
+extern void mDNSPlatformCloseDNSProxySkts(mDNS *const m);
+extern void mDNSPlatformDisposeProxyContext(void *context);
+extern mDNSu8 *DNSProxySetAttributes(DNSQuestion *q, DNSMessageHeader *h, DNSMessage *msg, mDNSu8 *start, mDNSu8 *limit);
+
+// Sleep Assertions are specific to Mac OS X
 #if APPLE_OSX_mDNSResponder
 #if APPLE_OSX_mDNSResponder
-extern mStatus ActivateLocalProxy(mDNS *const m, char *ifname);
+extern void mDNSPlatformSleepAssertion(mDNS *const m, double timeout);
 #endif
 
 #endif
 
+extern mDNSBool mDNSPlatformAllowPID(mDNS *const m, DNSQuestion *q);
+extern mDNSs32 mDNSPlatformGetServiceID(mDNS *const m, DNSQuestion *q);
+extern void mDNSPlatformSetDelegatePID(UDPSocket *src, const mDNSAddr *dst, DNSQuestion *q);
+extern mDNSs32 mDNSPlatformGetPID(void);
+
 // ***************************************************************************
 #if 0
 #pragma mark -
 // ***************************************************************************
 #if 0
 #pragma mark -
@@ -3051,6 +3463,13 @@ typedef enum
     mDNSSleepProxyMetric_IncidentalSoftware = 80
 } mDNSSleepProxyMetric;
 
     mDNSSleepProxyMetric_IncidentalSoftware = 80
 } mDNSSleepProxyMetric;
 
+typedef enum
+{
+    mDNS_NoWake        = 0, // System does not support Wake on LAN
+    mDNS_WakeOnAC      = 1, // System supports Wake on LAN when connected to AC power only
+    mDNS_WakeOnBattery = 2  // System supports Wake on LAN on battery
+} mDNSWakeForNetworkAccess;
+
 extern void mDNSCoreBeSleepProxyServer_internal(mDNS *const m, mDNSu8 sps, mDNSu8 port, mDNSu8 marginalpower, mDNSu8 totpower, mDNSu8 features);
 #define mDNSCoreBeSleepProxyServer(M,S,P,MP,TP,F)                       \
     do { mDNS_Lock(m); mDNSCoreBeSleepProxyServer_internal((M),(S),(P),(MP),(TP),(F)); mDNS_Unlock(m); } while(0)
 extern void mDNSCoreBeSleepProxyServer_internal(mDNS *const m, mDNSu8 sps, mDNSu8 port, mDNSu8 marginalpower, mDNSu8 totpower, mDNSu8 features);
 #define mDNSCoreBeSleepProxyServer(M,S,P,MP,TP,F)                       \
     do { mDNS_Lock(m); mDNSCoreBeSleepProxyServer_internal((M),(S),(P),(MP),(TP),(F)); mDNS_Unlock(m); } while(0)
@@ -3125,24 +3544,36 @@ struct CompileTimeAssertionChecks_mDNS
     // 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_RDataBody           [(sizeof(RDataBody)            ==   264) ? 1 : -1];
     // 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_RDataBody           [(sizeof(RDataBody)            ==   264) ? 1 : -1];
-    char sizecheck_ResourceRecord      [(sizeof(ResourceRecord)       <=    64) ? 1 : -1];
+    char sizecheck_ResourceRecord      [(sizeof(ResourceRecord)       <=    72) ? 1 : -1];
     char sizecheck_AuthRecord          [(sizeof(AuthRecord)           <=  1208) ? 1 : -1];
     char sizecheck_AuthRecord          [(sizeof(AuthRecord)           <=  1208) ? 1 : -1];
-    char sizecheck_CacheRecord         [(sizeof(CacheRecord)          <=   216) ? 1 : -1];
-    char sizecheck_CacheGroup          [(sizeof(CacheGroup)           <=   216) ? 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_CacheRecord         [(sizeof(CacheRecord)          <=   232) ? 1 : -1];
+    char sizecheck_CacheGroup          [(sizeof(CacheGroup)           <=   232) ? 1 : -1];
+    char sizecheck_DNSQuestion         [(sizeof(DNSQuestion)          <=   832) ? 1 : -1];
+
+// Checks commented out when sizeof(DNSQuestion) change cascaded into having to change yet another
+// set of hardcoded size values because these structures contain one or more DNSQuestion
+// instances.
+//     char sizecheck_ZoneData            [(sizeof(ZoneData)             <=  1648) ? 1 : -1];
+    char sizecheck_NATTraversalInfo    [(sizeof(NATTraversalInfo)     <=   200) ? 1 : -1];
     char sizecheck_HostnameInfo        [(sizeof(HostnameInfo)         <=  3050) ? 1 : -1];
     char sizecheck_HostnameInfo        [(sizeof(HostnameInfo)         <=  3050) ? 1 : -1];
-    char sizecheck_DNSServer           [(sizeof(DNSServer)            <=   328) ? 1 : -1];
-    char sizecheck_NetworkInterfaceInfo[(sizeof(NetworkInterfaceInfo) <=  6850) ? 1 : -1];
-    char sizecheck_ServiceRecordSet    [(sizeof(ServiceRecordSet)     <=  5500) ? 1 : -1];
+    char sizecheck_DNSServer           [(sizeof(DNSServer)            <=   340) ? 1 : -1];
+//    char sizecheck_NetworkInterfaceInfo[(sizeof(NetworkInterfaceInfo) <=  6988) ? 1 : -1];
+    char sizecheck_ServiceRecordSet    [(sizeof(ServiceRecordSet)     <=  5540) ? 1 : -1];
     char sizecheck_DomainAuthInfo      [(sizeof(DomainAuthInfo)       <=  7888) ? 1 : -1];
     char sizecheck_DomainAuthInfo      [(sizeof(DomainAuthInfo)       <=  7888) ? 1 : -1];
-    char sizecheck_ServiceInfoQuery    [(sizeof(ServiceInfoQuery)     <=  3200) ? 1 : -1];
+//    char sizecheck_ServiceInfoQuery    [(sizeof(ServiceInfoQuery)     <=  3302) ? 1 : -1];
 #if APPLE_OSX_mDNSResponder
 #if APPLE_OSX_mDNSResponder
-    char sizecheck_ClientTunnel        [(sizeof(ClientTunnel)         <=  1148) ? 1 : -1];
+//    char sizecheck_ClientTunnel        [(sizeof(ClientTunnel)         <=  1160) ? 1 : -1];
 #endif
 };
 
 #endif
 };
 
+// Routine to initialize device-info TXT record contents
+mDNSu32 initializeDeviceInfoTXT(mDNS *m, mDNSu8 *ptr);
+
+#if APPLE_OSX_mDNSResponder
+extern void D2D_start_advertising_interface(NetworkInterfaceInfo *interface);
+extern void D2D_stop_advertising_interface(NetworkInterfaceInfo *interface);
+#endif
+
 // ***************************************************************************
 
 #ifdef __cplusplus
 // ***************************************************************************
 
 #ifdef __cplusplus
index b2309712cc3e7cef252fb465d64f34ecec9dc684..a9f16b3fe1f730a332704ce2a6e34c479a08582d 100644 (file)
 #include "mDNSEmbeddedAPI.h"
 #include "DNSCommon.h"
 #include "nsec.h"
 #include "mDNSEmbeddedAPI.h"
 #include "DNSCommon.h"
 #include "nsec.h"
+#include "nsec3.h"
+
+// Define DNSSEC_DISABLED to remove all the DNSSEC functionality
+// and use the stub functions implemented later in this file.
+
+#ifndef DNSSEC_DISABLED
 
 // Implementation Notes
 //
 
 // Implementation Notes
 //
@@ -67,6 +73,13 @@ mDNSlocal CacheRecord *NSECParentForQuestion(mDNS *const m, DNSQuestion *q)
     return mDNSNULL;
 }
 
     return mDNSNULL;
 }
 
+mDNSlocal void UpdateParent(DNSSECVerifier *dv)
+{
+    AuthChainLink(dv->parent, dv->ac);
+    ResetAuthChain(dv);
+    dv->parent->NumPackets += dv->NumPackets;
+}
+
 // Note: This should just call the parent callback which will free the DNSSECVerifier.
 mDNSlocal void VerifyNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status)
 {
 // Note: This should just call the parent callback which will free the DNSSECVerifier.
 mDNSlocal void VerifyNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status)
 {
@@ -78,13 +91,21 @@ mDNSlocal void VerifyNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatu
     }
     if (dv->ac)
     {
     }
     if (dv->ac)
     {
-        // Before we call the callback, we need to update the
+        // Before we free the "dv", we need to update the
         // parent with our AuthChain information
         // parent with our AuthChain information
-        AuthChainLink(dv->parent, dv->ac);
-        dv->ac = mDNSNULL;
-        dv->actail = &dv->ac;
+        UpdateParent(dv);
+    }
+    // "status" indicates whether we are able to successfully verify
+    // the NSEC/NSEC3 signatures. For NSEC3, the OptOut flag may be set
+    // for which we need to deliver insecure result.
+    if ((dv->parent->flags & NSEC3_OPT_OUT) && (status == DNSSEC_Secure))
+    {
+        dv->parent->DVCallback(m, dv->parent, DNSSEC_Insecure);
+    }
+    else
+    {
+        dv->parent->DVCallback(m, dv->parent, status);
     }
     }
-    dv->parent->DVCallback(m, dv->parent, status);
     // The callback we called in the previous line should recursively
     // free all the DNSSECVerifiers starting from dv->parent and above.
     // So, set that to NULL and free the "dv" itself here.
     // The callback we called in the previous line should recursively
     // free all the DNSSECVerifiers starting from dv->parent and above.
     // So, set that to NULL and free the "dv" itself here.
@@ -108,8 +129,7 @@ mDNSlocal void VerifyNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatu
 // them based on the name hash like other records as in most cases the returned NSECs has a different name than we asked for
 // (except for NODATA error where the name exists but type does not exist).
 //
 // them based on the name hash like other records as in most cases the returned NSECs has a different name than we asked for
 // (except for NODATA error where the name exists but type does not exist).
 //
-mDNSlocal void VerifyNSEC(mDNS *const m, ResourceRecord *rr, RRVerifier *rv, DNSSECVerifier *pdv, CacheRecord *ncr,
-                          DNSSECVerifierCallback callback)
+mDNSexport void VerifyNSEC(mDNS *const m, ResourceRecord *rr, RRVerifier *rv, DNSSECVerifier *pdv, CacheRecord *ncr, DNSSECVerifierCallback callback)
 {
     DNSSECVerifier *dv = mDNSNULL;
     CacheRecord **rp;
 {
     DNSSECVerifier *dv = mDNSNULL;
     CacheRecord **rp;
@@ -142,8 +162,13 @@ mDNSlocal void VerifyNSEC(mDNS *const m, ResourceRecord *rr, RRVerifier *rv, DNS
         rrtype = rv->rrtype;
     }
 
         rrtype = rv->rrtype;
     }
 
-    dv = AllocateDNSSECVerifier(m, name, rrtype, pdv->q.InterfaceID, (callback ? callback : VerifyNSECCallback), mDNSNULL);
-    if (!dv) { LogMsg("VerifyNSEC: mDNSPlatformMemAlloc failed"); return; }
+    dv = AllocateDNSSECVerifier(m, name, rrtype, pdv->q.InterfaceID, DNSSEC_VALIDATION_SECURE,
+        (callback ? callback : VerifyNSECCallback), mDNSNULL);
+    if (!dv)
+    {
+        LogMsg("VerifyNSEC: mDNSPlatformMemAlloc failed");
+        return;
+    }
 
     dv->parent = pdv;
 
 
     dv->parent = pdv;
 
@@ -164,11 +189,14 @@ mDNSlocal void VerifyNSEC(mDNS *const m, ResourceRecord *rr, RRVerifier *rv, DNS
         rp=&(*rp)->next;
     }
 
         rp=&(*rp)->next;
     }
 
-    if (!dv->rrset || !dv->rrsig)
+    if (!dv->rrset)
     {
     {
-        LogMsg("VerifyNSEC: ERROR!! AddRRSetToVerifier missing rrset %p, rrsig %p", dv->rrset, dv->rrsig);
+        LogMsg("VerifyNSEC: ERROR!! AddRRSetToVerifier missing rrset");
         goto error;
     }
         goto error;
     }
+    // Expired signatures.
+    if (!dv->rrsig)
+        goto error;
 
     // Next step is to fetch the keys
     dv->next = RRVS_key;
 
     // Next step is to fetch the keys
     dv->next = RRVS_key;
@@ -176,7 +204,7 @@ mDNSlocal void VerifyNSEC(mDNS *const m, ResourceRecord *rr, RRVerifier *rv, DNS
     StartDNSSECVerification(m, dv);
     return;
 error:
     StartDNSSECVerification(m, dv);
     return;
 error:
-    pdv->DVCallback(m, pdv, DNSSEC_Indeterminate);
+    pdv->DVCallback(m, pdv, DNSSEC_Bogus);
     if (dv)
     {
         dv->parent = mDNSNULL;
     if (dv)
     {
         dv->parent = mDNSNULL;
@@ -202,7 +230,9 @@ mDNSlocal void DeleteCachedNSECS(mDNS *const m, CacheRecord *cr)
 // failure (mDNSfalse)
 mDNSexport mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, CacheRecord *negcr, mDNSu8 rcode)
 {
 // failure (mDNSfalse)
 mDNSexport mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, CacheRecord *negcr, mDNSu8 rcode)
 {
-    CacheRecord *cr, *next;
+    CacheRecord *cr;
+    mDNSBool nsecs_seen = mDNSfalse;
+    mDNSBool nsec3s_seen = mDNSfalse;
 
     if (rcode != kDNSFlag1_RC_NoErr && rcode != kDNSFlag1_RC_NXDomain)
     {
 
     if (rcode != kDNSFlag1_RC_NoErr && rcode != kDNSFlag1_RC_NXDomain)
     {
@@ -214,8 +244,8 @@ mDNSexport mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, C
     // NSECs and its RRSIGs
     for (cr = crlist; cr; cr = cr->next)
     {
     // NSECs and its RRSIGs
     for (cr = crlist; cr; cr = cr->next)
     {
-        next = cr->next;
-        if (cr->resrec.rrtype != kDNSType_NSEC && cr->resrec.rrtype != kDNSType_RRSIG)
+        if (cr->resrec.rrtype != kDNSType_NSEC && cr->resrec.rrtype != kDNSType_NSEC3 &&
+            cr->resrec.rrtype != kDNSType_SOA && cr->resrec.rrtype != kDNSType_RRSIG)
         {
             LogMsg("AddNSECSForCacheRecord: ERROR!! Adding Wrong record %s", CRDisplayString(m, cr));
             return mDNSfalse;
         {
             LogMsg("AddNSECSForCacheRecord: ERROR!! Adding Wrong record %s", CRDisplayString(m, cr));
             return mDNSfalse;
@@ -224,24 +254,37 @@ mDNSexport mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, C
         {
             RDataBody2 *const rdb = (RDataBody2 *)cr->smallrdatastorage.data;
             rdataRRSig *rrsig = &rdb->rrsig;
         {
             RDataBody2 *const rdb = (RDataBody2 *)cr->smallrdatastorage.data;
             rdataRRSig *rrsig = &rdb->rrsig;
-            if (swap16(rrsig->typeCovered) != kDNSType_NSEC)
+            mDNSu16 tc = swap16(rrsig->typeCovered);
+            if (tc != kDNSType_NSEC && tc != kDNSType_NSEC3 && tc != kDNSType_SOA)
             {
                 LogMsg("AddNSECSForCacheRecord:ERROR!! Adding RRSIG with Wrong type %s", CRDisplayString(m, cr));
                 return mDNSfalse;
             }
         }
             {
                 LogMsg("AddNSECSForCacheRecord:ERROR!! Adding RRSIG with Wrong type %s", CRDisplayString(m, cr));
                 return mDNSfalse;
             }
         }
+        else if (cr->resrec.rrtype == kDNSType_NSEC)
+        {
+            nsecs_seen = mDNStrue;
+        }
+        else if (cr->resrec.rrtype == kDNSType_NSEC3)
+        {
+            nsec3s_seen = mDNStrue;
+        }
         LogDNSSEC("AddNSECSForCacheRecord: Found a valid record %s", CRDisplayString(m, cr));
     }
         LogDNSSEC("AddNSECSForCacheRecord: Found a valid record %s", CRDisplayString(m, cr));
     }
+    if ((nsecs_seen && nsec3s_seen) || (!nsecs_seen && !nsec3s_seen))
+    {
+        LogDNSSEC("AddNSECSForCacheRecord:ERROR  nsecs_seen %d, nsec3s_seen %d", nsecs_seen, nsec3s_seen);
+        return mDNSfalse;
+    }
     DeleteCachedNSECS(m, negcr);
     LogDNSSEC("AddNSECSForCacheRecord: Adding NSEC Records for %s", CRDisplayString(m, negcr));
     negcr->nsec = crlist;
     DeleteCachedNSECS(m, negcr);
     LogDNSSEC("AddNSECSForCacheRecord: Adding NSEC Records for %s", CRDisplayString(m, negcr));
     negcr->nsec = crlist;
-    negcr->rcode = rcode;
     return mDNStrue;
 }
 
 // Return the number of labels that matches starting from the right (excluding the
 // root label)
     return mDNStrue;
 }
 
 // Return the number of labels that matches starting from the right (excluding the
 // root label)
-mDNSlocal int CountLabelsMatch(const domainname *const d1, const domainname *const d2)
+mDNSexport int CountLabelsMatch(const domainname *const d1, const domainname *const d2)
 {
     int count, c1, c2;
     int match, i, skip1, skip2;
 {
     int count, c1, c2;
     int match, i, skip1, skip2;
@@ -271,98 +314,6 @@ mDNSlocal int CountLabelsMatch(const domainname *const d1, const domainname *con
     return match;
 }
 
     return match;
 }
 
-// RFC 4034:
-//
-// Section 6.1:
-//
-// For the purposes of DNS security, owner names are ordered by treating
-// individual labels as unsigned left-justified octet strings.  The
-// absence of a octet sorts before a zero value octet, and uppercase
-// US-ASCII letters are treated as if they were lowercase US-ASCII
-// letters.
-//
-// To compute the canonical ordering of a set of DNS names, start by
-// sorting the names according to their most significant (rightmost)
-// labels.  For names in which the most significant label is identical,
-// continue sorting according to their next most significant label, and
-// so forth.
-//
-// Returns 0 if the names are same
-// Returns -1 if d1 < d2
-// Returns  1 if d1 > d2
-//
-// subdomain is set if there is at least one label match (starting from the end)
-// and d1 has more labels than d2 e.g., a.b.com is a subdomain of b.com
-//
-mDNSlocal int DNSSECCanonicalOrder(const domainname *const d1, const domainname *const d2, int *subdomain)
-{
-    int count, c1, c2;
-    int i, skip1, skip2;
-
-    c1 = CountLabels(d1);
-    skip1 = c1 - 1;
-    c2 = CountLabels(d2);
-    skip2 = c2 - 1;
-
-    if (subdomain) *subdomain = 0;
-
-    // Compare as many labels as possible starting from the rightmost
-    count = c1 < c2 ? c1 : c2;
-    for (i = count; i > 0; i--)
-    {
-        mDNSu8 *a, *b;
-        int j, len, lena, lenb;
-
-        a = (mDNSu8 *)SkipLeadingLabels(d1, skip1);
-        b = (mDNSu8 *)SkipLeadingLabels(d2, skip2);
-        lena = *a;
-        lenb = *b;
-        // Compare label by label. Note that "z" > "yak" because z > y, but z < za
-        // (lena - lenb check below) because 'za' has two characters. Hence compare the
-        // letters first and then compare the length of the label at the end.
-        len = lena < lenb ? lena : lenb;
-        a++; b++;
-        for (j = 0; j < len; j++)
-        {
-            mDNSu8 ac = *a++;
-            mDNSu8 bc = *b++;
-            if (mDNSIsUpperCase(ac)) ac += 'a' - 'A';
-            if (mDNSIsUpperCase(bc)) bc += 'a' - 'A';
-            if (ac != bc)
-            {
-                verbosedebugf("DNSSECCanonicalOrder: returning ac %c, bc %c", ac, bc);
-                return ((ac < bc) ? -1 : 1);
-            }
-        }
-        if ((lena - lenb) != 0)
-        {
-            verbosedebugf("DNSSECCanonicalOrder: returning lena %d lenb %d", lena, lenb);
-            return ((lena < lenb) ? -1 : 1);
-        }
-
-        // Continue with the next label
-        skip1--;
-        skip2--;
-    }
-    // We have compared label by label. Both of them are same if we are here.
-    //
-    // Two possibilities.
-    //
-    // 1) Both names have same number of labels. In that case, return zero.
-    // 2) The number of labels is not same. As zero label sorts before, names
-    //    with more number of labels is greater.
-
-    // a.b.com is a subdomain of b.com
-    if ((c1 > c2) && subdomain)
-        *subdomain = 1;
-
-    verbosedebugf("DNSSECCanonicalOrder: returning c1 %d c2 %d\n", c1, c2);
-    if (c1 != c2)
-        return ((c1 < c2) ? -1 : 1);
-    else
-        return 0;
-}
-
 // Empty Non-Terminal (ENT): if the qname is bigger than nsec owner's name and a
 // subdomain of the nsec's nxt field, then the qname is a empty non-terminal. For
 // example, if you are looking for (in RFC 4035 example zone) "y.w.example  A"
 // Empty Non-Terminal (ENT): if the qname is bigger than nsec owner's name and a
 // subdomain of the nsec's nxt field, then the qname is a empty non-terminal. For
 // example, if you are looking for (in RFC 4035 example zone) "y.w.example  A"
@@ -373,7 +324,7 @@ mDNSlocal int DNSSECCanonicalOrder(const domainname *const d1, const domainname
 // This function is normally called before checking for wildcard matches. If you
 // find this NSEC, there is no need to look for a wildcard record
 // that could possibly answer the question.
 // This function is normally called before checking for wildcard matches. If you
 // find this NSEC, there is no need to look for a wildcard record
 // that could possibly answer the question.
-mDNSexport mDNSBool NSECAnswersENT(const ResourceRecord *const rr, domainname *qname)
+mDNSlocal mDNSBool NSECAnswersENT(const ResourceRecord *const rr, domainname *qname)
 {
     const domainname *oname = rr->name;
     const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
 {
     const domainname *oname = rr->name;
     const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
@@ -456,13 +407,13 @@ mDNSlocal int NSECNameExists(mDNS *const m, ResourceRecord *rr, domainname *name
 
         // We are here because the owner name is the same as "name". Make sure the
         // NSEC has the right NS and SOA bits set.
 
         // We are here because the owner name is the same as "name". Make sure the
         // NSEC has the right NS and SOA bits set.
-        if (ns && !soa && qtype != kDNSType_DS)
+        if (qtype != kDNSType_DS && ns && !soa)
         {
             LogDNSSEC("NSECNameExists: Parent side NSEC %s can't be used for question %##s (%s)",
                       RRDisplayString(m, rr), name->c, DNSTypeName(qtype));
             return -1;
         }
         {
             LogDNSSEC("NSECNameExists: Parent side NSEC %s can't be used for question %##s (%s)",
                       RRDisplayString(m, rr), name->c, DNSTypeName(qtype));
             return -1;
         }
-        else if (ns && soa && qtype == kDNSType_DS)
+        else if (qtype == kDNSType_DS && soa)
         {
             LogDNSSEC("NSECNameExists: Child side NSEC %s can't be used for question %##s (%s)",
                       RRDisplayString(m, rr), name->c, DNSTypeName(qtype));
         {
             LogDNSSEC("NSECNameExists: Child side NSEC %s can't be used for question %##s (%s)",
                       RRDisplayString(m, rr), name->c, DNSTypeName(qtype));
@@ -568,6 +519,7 @@ mDNSexport void WildcardAnswerProof(mDNS *const m, DNSSECVerifier *dv)
     CacheRecord **rp;
     const domainname *ce;
     DNSQuestion q;
     CacheRecord **rp;
     const domainname *ce;
     DNSQuestion q;
+    CacheRecord **nsec3 = mDNSNULL;
 
     LogDNSSEC("WildcardAnswerProof: Question %##s (%s)", dv->origName.c, DNSTypeName(dv->origType));
     //
 
     LogDNSSEC("WildcardAnswerProof: Question %##s (%s)", dv->origName.c, DNSTypeName(dv->origType));
     //
@@ -591,9 +543,13 @@ mDNSexport void WildcardAnswerProof(mDNS *const m, DNSSECVerifier *dv)
     ncr = NSECParentForQuestion(m, &q);
     if (!ncr)
     {
     ncr = NSECParentForQuestion(m, &q);
     if (!ncr)
     {
-        LogMsg("NSECWildCardProof: Can't find NSEC Parent for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
+        LogMsg("WildcardAnswerProof: Can't find NSEC Parent for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
         goto error;
     }
         goto error;
     }
+    else
+    {
+        LogDNSSEC("WildcardAnswerProof: found %s", CRDisplayString(m, ncr));
+    }
     rp = &(ncr->nsec);
     while (*rp)
     {
     rp = &(ncr->nsec);
     while (*rp)
     {
@@ -603,29 +559,45 @@ mDNSexport void WildcardAnswerProof(mDNS *const m, DNSSECVerifier *dv)
             if (!NSECNameExists(m, &cr->resrec, &dv->origName, dv->origType))
                 break;
         }
             if (!NSECNameExists(m, &cr->resrec, &dv->origName, dv->origType))
                 break;
         }
+        else if ((*rp)->resrec.rrtype == kDNSType_NSEC3)
+        {
+            nsec3 = rp;
+        }
         rp=&(*rp)->next;
     }
     if (!(*rp))
     {
         rp=&(*rp)->next;
     }
     if (!(*rp))
     {
-        LogMsg("NSECWildCardProof: ERROR!! No  NSECs found for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
-        goto error;
-    }
-    ce = NSECClosestEncloser(&((*rp)->resrec), &dv->origName);
-    if (!ce)
-    {
-        LogMsg("NSECWildCardProof: ERROR!! Closest Encloser NULL for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
-        goto error;
+        mDNSBool ret = mDNSfalse;
+        if (nsec3)
+        {
+            ret = NSEC3WildcardAnswerProof(m, ncr, dv);
+        }
+        if (!ret)
+        {
+            LogDNSSEC("WildcardAnswerProof: NSEC3 wildcard proof failed for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
+            goto error;
+        }
+        rp = nsec3;
     }
     }
-    if (!SameDomainName(ce, dv->wildcardName))
+    else
     {
     {
-        LogMsg("NSECWildCardProof: ERROR!! Closest Encloser %##s does not match wildcard name %##s", q.qname.c, dv->wildcardName->c);
-        goto error;
+        ce = NSECClosestEncloser(&((*rp)->resrec), &dv->origName);
+        if (!ce)
+        {
+            LogMsg("WildcardAnswerProof: ERROR!! Closest Encloser NULL for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
+            goto error;
+        }
+        if (!SameDomainName(ce, dv->wildcardName))
+        {
+            LogMsg("WildcardAnswerProof: ERROR!! Closest Encloser %##s does not match wildcard name %##s", q.qname.c, dv->wildcardName->c);
+            goto error;
+        }
     }
 
     VerifyNSEC(m, &((*rp)->resrec), mDNSNULL, dv, ncr, mDNSNULL);
     return;
 error:
     }
 
     VerifyNSEC(m, &((*rp)->resrec), mDNSNULL, dv, ncr, mDNSNULL);
     return;
 error:
-    dv->DVCallback(m, dv, DNSSEC_Insecure);
+    dv->DVCallback(m, dv, DNSSEC_Bogus);
 }
 
 // We have a NSEC. Need to see if it proves that NODATA exists for the given name. Note that this
 }
 
 // We have a NSEC. Need to see if it proves that NODATA exists for the given name. Note that this
@@ -669,7 +641,7 @@ mDNSlocal mDNSBool NSECNoDataError(mDNS *const m, ResourceRecord *rr, domainname
         }
         else
         {
         }
         else
         {
-            if (ns && soa)
+            if (soa)
             {
                 LogDNSSEC("NSECNoDataError: Child side NSEC %s, can't use for parent qname %##s (%s)",
                           RRDisplayString(m, rr), name->c, DNSTypeName(qtype));
             {
                 LogDNSSEC("NSECNoDataError: Child side NSEC %s, can't use for parent qname %##s (%s)",
                           RRDisplayString(m, rr), name->c, DNSTypeName(qtype));
@@ -688,10 +660,9 @@ mDNSlocal mDNSBool NSECNoDataError(mDNS *const m, ResourceRecord *rr, domainname
     {
         // Name does not exist. Before we check for a wildcard match, make sure that
         // this is not an ENT.
     {
         // Name does not exist. Before we check for a wildcard match, make sure that
         // this is not an ENT.
-        //
         if (NSECAnswersENT(rr, name))
         {
         if (NSECAnswersENT(rr, name))
         {
-            LogDNSSEC("NSECNoDataError: ERROR!! name %##s exists %s", name->c, RRDisplayString(m, rr));
+            LogDNSSEC("NSECNoDataError: name %##s exists %s", name->c, RRDisplayString(m, rr));
             return mDNSfalse;
         }
 
             return mDNSfalse;
         }
 
@@ -716,17 +687,16 @@ mDNSlocal mDNSBool NSECNoDataError(mDNS *const m, ResourceRecord *rr, domainname
                     LogMsg("NSECNoDataError: ERROR!! qtype %s exists in wildcard %s", DNSTypeName(qtype), RRDisplayString(m, rr));
                     return mDNSfalse;
                 }
                     LogMsg("NSECNoDataError: ERROR!! qtype %s exists in wildcard %s", DNSTypeName(qtype), RRDisplayString(m, rr));
                     return mDNSfalse;
                 }
-                // It is odd for a wildcard to match when we are looking up DS
-                // See RFC 4592
-                if (qtype == kDNSType_DS)
+                if (qtype == kDNSType_DS && RRAssertsExistence(rr, kDNSType_SOA))
                 {
                 {
-                    LogMsg("NSECNoDataError: ERROR!! DS qtype exists in wildcard %s", RRDisplayString(m, rr));
+                    LogDNSSEC("NSECNoDataError: Child side wildcard NSEC %s, can't use for parent qname %##s (%s)",
+                              RRDisplayString(m, rr), name->c, DNSTypeName(qtype));
                     return mDNSfalse;
                 }
                     return mDNSfalse;
                 }
-                // Don't use the parent side record for this
-                if (RRAssertsNonexistence(rr, kDNSType_SOA) &&
+                else if (qtype != kDNSType_DS && RRAssertsNonexistence(rr, kDNSType_SOA) &&
                     RRAssertsExistence(rr, kDNSType_NS))
                 {
                     RRAssertsExistence(rr, kDNSType_NS))
                 {
+                    // Don't use the parent side record for this
                     LogDNSSEC("NSECNoDataError: Parent side wildcard NSEC %s, can't use for child qname %##s (%s)",
                               RRDisplayString(m, rr), name->c, DNSTypeName(qtype));
                     return mDNSfalse;
                     LogDNSSEC("NSECNoDataError: Parent side wildcard NSEC %s, can't use for child qname %##s (%s)",
                               RRDisplayString(m, rr), name->c, DNSTypeName(qtype));
                     return mDNSfalse;
@@ -740,7 +710,7 @@ mDNSlocal mDNSBool NSECNoDataError(mDNS *const m, ResourceRecord *rr, domainname
     }
 }
 
     }
 }
 
-mDNSlocal void NoDataNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status)
+mDNSexport void NoDataNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status)
 {
     RRVerifier *rv;
     DNSSECVerifier *pdv;
 {
     RRVerifier *rv;
     DNSSECVerifier *pdv;
@@ -758,32 +728,21 @@ mDNSlocal void NoDataNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatu
     {
         // Before we free the "dv", we need to update the
         // parent with our AuthChain information
     {
         // Before we free the "dv", we need to update the
         // parent with our AuthChain information
-        AuthChainLink(dv->parent, dv->ac);
-        dv->ac = mDNSNULL;
-        dv->actail = &dv->ac;
+        UpdateParent(dv);
     }
 
     pdv = dv->parent;
     }
 
     pdv = dv->parent;
+
+    // We don't care about the "dv" that was allocated in VerifyNSEC
+    // as it just verifies one of the nsecs. Get the original verifier and
+    // verify the other NSEC like we did the first time.
+    dv->parent = mDNSNULL;
+    FreeDNSSECVerifier(m, dv);
+
     if (status != DNSSEC_Secure)
     {
         goto error;
     }
     if (status != DNSSEC_Secure)
     {
         goto error;
     }
-    if (!(pdv->flags & NSEC_PROVES_NONAME_EXISTS))
-    {
-        LogMsg("NoDataNSECCCallback: ERROR!! NSEC_PROVES_NONAME_EXISTS not set");
-        goto error;
-    }
-    if (!(pdv->flags & WILDCARD_PROVES_NONAME_EXISTS))
-    {
-        LogMsg("NoDataNSECCCallback: ERROR!! WILDCARD_PROVES_NONAME_EXISTS not set");
-        goto error;
-    }
-
-    // We don't care about the "dv" that was allocated in VerifyNSEC.
-    // Get the original verifier and verify the other NSEC like we did
-    // the first time.
-    dv->parent = mDNSNULL;
-    FreeDNSSECVerifier(m, dv);
 
     ncr = NSECParentForQuestion(m, &pdv->q);
     if (!ncr)
 
     ncr = NSECParentForQuestion(m, &pdv->q);
     if (!ncr)
@@ -791,21 +750,23 @@ mDNSlocal void NoDataNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatu
         LogMsg("NoDataNSECCallback: Can't find NSEC Parent for %##s (%s)", pdv->q.qname.c, DNSTypeName(pdv->q.qtype));
         goto error;
     }
         LogMsg("NoDataNSECCallback: Can't find NSEC Parent for %##s (%s)", pdv->q.qname.c, DNSTypeName(pdv->q.qtype));
         goto error;
     }
-
     rv = pdv->pendingNSEC;
     rv = pdv->pendingNSEC;
-    pdv->pendingNSEC = mDNSNULL;
-    // Verify the pendingNSEC and we don't need to come back here. Let the regular
-    // NSECCallback call the original callback.
-    VerifyNSEC(m, mDNSNULL, rv, pdv, ncr, mDNSNULL);
+    pdv->pendingNSEC = rv->next;
+    // We might have more than one pendingNSEC in the case of NSEC3. If this is the last one,
+    // we don't need to come back here; let the regular NSECCallback call the original callback.
+    rv->next = mDNSNULL;
+    LogDNSSEC("NoDataNSECCallback: Verifying %##s (%s)", rv->name.c, DNSTypeName(rv->rrtype));
+    if (!pdv->pendingNSEC)
+        VerifyNSEC(m, mDNSNULL, rv, pdv, ncr, mDNSNULL);
+    else
+        VerifyNSEC(m, mDNSNULL, rv, pdv, ncr, NoDataNSECCallback);
     return;
 
 error:
     return;
 
 error:
-    dv->parent->DVCallback(m, dv->parent, status);
-    dv->parent = mDNSNULL;
-    FreeDNSSECVerifier(m, dv);
+    pdv->DVCallback(m, pdv, status);
 }
 
 }
 
-mDNSlocal void NameErrorNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status)
+mDNSexport void NameErrorNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status)
 {
     RRVerifier *rv;
     DNSSECVerifier *pdv;
 {
     RRVerifier *rv;
     DNSSECVerifier *pdv;
@@ -823,21 +784,20 @@ mDNSlocal void NameErrorNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECSt
     {
         // Before we free the "dv", we need to update the
         // parent with our AuthChain information
     {
         // Before we free the "dv", we need to update the
         // parent with our AuthChain information
-        AuthChainLink(dv->parent, dv->ac);
-        dv->ac = mDNSNULL;
-        dv->actail = &dv->ac;
+        UpdateParent(dv);
     }
 
     pdv = dv->parent;
     }
 
     pdv = dv->parent;
+    // We don't care about the "dv" that was allocated in VerifyNSEC
+    // as it just verifies one of the nsecs. Get the original verifier and
+    // verify the other NSEC like we did the first time.
+    dv->parent = mDNSNULL;
+    FreeDNSSECVerifier(m, dv);
+
     if (status != DNSSEC_Secure)
     {
         goto error;
     }
     if (status != DNSSEC_Secure)
     {
         goto error;
     }
-    // We don't care about the "dv" that was allocated in VerifyNSEC.
-    // Get the original verifier and verify the other NSEC like we did
-    // the first time.
-    dv->parent = mDNSNULL;
-    FreeDNSSECVerifier(m, dv);
 
     ncr = NSECParentForQuestion(m, &pdv->q);
     if (!ncr)
 
     ncr = NSECParentForQuestion(m, &pdv->q);
     if (!ncr)
@@ -846,16 +806,20 @@ mDNSlocal void NameErrorNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECSt
         goto error;
     }
     rv = pdv->pendingNSEC;
         goto error;
     }
     rv = pdv->pendingNSEC;
-    pdv->pendingNSEC = mDNSNULL;
-    // Verify the pendingNSEC and we don't need to come back here. Let the regular
-    // NSECCallback call the original callback.
-    VerifyNSEC(m, mDNSNULL, rv, pdv, ncr, mDNSNULL);
+    pdv->pendingNSEC = rv->next;
+    // We might have more than one pendingNSEC in the case of NSEC3. If this is the last one,
+    // we don't need to come back here; let the regular NSECCallback call the original callback.
+    rv->next = mDNSNULL;
+    LogDNSSEC("NameErrorNSECCallback: Verifying %##s (%s)", rv->name.c, DNSTypeName(rv->rrtype));
+    if (!pdv->pendingNSEC)
+        VerifyNSEC(m, mDNSNULL, rv, pdv, ncr, mDNSNULL);
+    else
+        VerifyNSEC(m, mDNSNULL, rv, pdv, ncr, NameErrorNSECCallback);
+
     return;
 
 error:
     return;
 
 error:
-    dv->parent->DVCallback(m, dv->parent, status);
-    dv->parent = mDNSNULL;
-    FreeDNSSECVerifier(m, dv);
+    pdv->DVCallback(m, pdv, status);
 }
 
 // We get a NODATA error with no records in answer section. This proves
 }
 
 // We get a NODATA error with no records in answer section. This proves
@@ -910,6 +874,11 @@ mDNSlocal void NoDataProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr)
         }
         rp=&(*rp)->next;
     }
         }
         rp=&(*rp)->next;
     }
+    if (!nsec_noname && !nsec_wild)
+    {
+        LogDNSSEC("NoDataProof: No valid NSECs for %##s (%s)", dv->q.qname.c, DNSTypeName(dv->q.qtype));
+        goto error;
+    }
     // If the type exists, then we have to verify just that NSEC
     if (!(dv->flags & NSEC_PROVES_NOTYPE_EXISTS))
     {
     // If the type exists, then we have to verify just that NSEC
     if (!(dv->flags & NSEC_PROVES_NOTYPE_EXISTS))
     {
@@ -926,6 +895,12 @@ mDNSlocal void NoDataProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr)
             LogMsg("NoDataProof: wildcard %##s does not match closest encloser %##s", wildcard->c, ce->c);
             goto error;
         }
             LogMsg("NoDataProof: wildcard %##s does not match closest encloser %##s", wildcard->c, ce->c);
             goto error;
         }
+        // If a single NSEC can prove both, then we just have validate that one NSEC.
+        if (nsec_wild == nsec_noname)
+        {
+            nsec_noname = mDNSNULL;
+            dv->flags &= ~NSEC_PROVES_NONAME_EXISTS;
+        }
     }
 
     if ((dv->flags & (WILDCARD_PROVES_NONAME_EXISTS|NSEC_PROVES_NONAME_EXISTS)) ==
     }
 
     if ((dv->flags & (WILDCARD_PROVES_NONAME_EXISTS|NSEC_PROVES_NONAME_EXISTS)) ==
@@ -937,21 +912,24 @@ mDNSlocal void NoDataProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr)
         // First verify wildcard NSEC and then when we are done, we
         // will verify the noname nsec
         dv->pendingNSEC = r;
         // First verify wildcard NSEC and then when we are done, we
         // will verify the noname nsec
         dv->pendingNSEC = r;
+        LogDNSSEC("NoDataProof: Verifying wild and noname %s", RRDisplayString(m, nsec_wild));
         VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, NoDataNSECCallback);
     }
     else if ((dv->flags & WILDCARD_PROVES_NONAME_EXISTS) ||
              (dv->flags & NSEC_PROVES_NOTYPE_EXISTS))
     {
         VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, NoDataNSECCallback);
     }
     else if ((dv->flags & WILDCARD_PROVES_NONAME_EXISTS) ||
              (dv->flags & NSEC_PROVES_NOTYPE_EXISTS))
     {
+        LogDNSSEC("NoDataProof: Verifying wild %s", RRDisplayString(m, nsec_wild));
         VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, mDNSNULL);
     }
     else if (dv->flags & NSEC_PROVES_NONAME_EXISTS)
     {
         VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, mDNSNULL);
     }
     else if (dv->flags & NSEC_PROVES_NONAME_EXISTS)
     {
+        LogDNSSEC("NoDataProof: Verifying noname %s", RRDisplayString(m, nsec_noname));
         VerifyNSEC(m, nsec_noname, mDNSNULL, dv, ncr, mDNSNULL);
     }
     return;
 error:
     LogDNSSEC("NoDataProof: Error return");
         VerifyNSEC(m, nsec_noname, mDNSNULL, dv, ncr, mDNSNULL);
     }
     return;
 error:
     LogDNSSEC("NoDataProof: Error return");
-    dv->DVCallback(m, dv, DNSSEC_Insecure);
+    dv->DVCallback(m, dv, DNSSEC_Bogus);
 }
 
 mDNSlocal mDNSBool NSECNoWildcard(mDNS *const m, ResourceRecord *rr, domainname *qname, mDNSu16 qtype)
 }
 
 mDNSlocal mDNSBool NSECNoWildcard(mDNS *const m, ResourceRecord *rr, domainname *qname, mDNSu16 qtype)
@@ -1055,77 +1033,234 @@ mDNSlocal void NameErrorProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *nc
         RRVerifier *r = AllocateRRVerifier(nsec_noname, &status);
         if (!r) goto error;
         dv->pendingNSEC = r;
         RRVerifier *r = AllocateRRVerifier(nsec_noname, &status);
         if (!r) goto error;
         dv->pendingNSEC = r;
+        LogDNSSEC("NoDataProof: Verifying wild %s", RRDisplayString(m, nsec_wild));
         VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, NameErrorNSECCallback);
     }
     else
     {
         VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, NameErrorNSECCallback);
     }
     else
     {
+        LogDNSSEC("NoDataProof: Verifying only one %s", RRDisplayString(m, nsec_wild));
         VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, mDNSNULL);
     }
     return;
 error:
         VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, mDNSNULL);
     }
     return;
 error:
-    dv->DVCallback(m, dv, DNSSEC_Insecure);
+    dv->DVCallback(m, dv, DNSSEC_Bogus);
+}
+
+mDNSexport CacheRecord *NSECRecordIsDelegation(mDNS *const m, domainname *name, mDNSu16 qtype)
+{
+    CacheGroup *cg;
+    CacheRecord *cr;
+    mDNSu32 slot, namehash;
+
+    slot = HashSlot(name);
+    namehash = DomainNameHashValue(name);
+
+    cg = CacheGroupForName(m, (const mDNSu32)slot, namehash, name);
+    if (!cg)
+    {
+        LogDNSSEC("NSECRecordForName: cg NULL for %##s", name);
+        return mDNSNULL;
+    }
+    for (cr = cg->members; cr; cr = cr->next)
+    {
+        if (cr->resrec.RecordType == kDNSRecordTypePacketNegative && cr->resrec.rrtype == qtype)
+        {
+            CacheRecord *ncr;
+            for (ncr = cr->nsec; ncr; ncr = ncr->next)
+            {
+                if (ncr->resrec.rrtype == kDNSType_NSEC &&
+                    SameDomainName(ncr->resrec.name, name))
+                {
+                    // See the Insecure Delegation Proof section in dnssec-bis: DS bit and SOA bit
+                    // should be absent
+                    if (RRAssertsExistence(&ncr->resrec, kDNSType_SOA) ||
+                        RRAssertsExistence(&ncr->resrec, kDNSType_DS))
+                    {
+                        LogDNSSEC("NSECRecordForName: found record %s for %##s (%s), but DS or SOA bit set", CRDisplayString(m, ncr), name,
+                            DNSTypeName(qtype));
+                        return mDNSNULL;
+                    }
+                    // Section 2.3 of RFC 4035 states that:
+                    //
+                    // Each owner name in the zone that has authoritative data or a delegation point NS RRset MUST
+                    // have an NSEC resource record. 
+                    //
+                    // So, if we have an NSEC record matching the question name with the NS bit set,
+                    // then this is a delegation.
+                    //
+                    if (RRAssertsExistence(&ncr->resrec, kDNSType_NS))
+                    {
+                        LogDNSSEC("NSECRecordForName: found record %s for %##s (%s)", CRDisplayString(m, ncr), name, DNSTypeName(qtype));
+                        return ncr;
+                    }
+                    else
+                    {
+                        LogDNSSEC("NSECRecordForName: found record %s for %##s (%s), but NS bit is not set", CRDisplayString(m, ncr), name,
+                            DNSTypeName(qtype));
+                        return mDNSNULL;
+                    }
+                }
+            }
+        }
+    }
+    return mDNSNULL;
+}
+
+mDNSlocal void StartInsecureProof(mDNS * const m, DNSSECVerifier *dv)
+{
+    domainname trigger;
+    DNSSECVerifier *prevdv = mDNSNULL;
+
+    // Remember the name that triggered the insecure proof
+    AssignDomainName(&trigger, &dv->q.qname);
+    while (dv->parent)
+    {
+        prevdv = dv;
+        dv = dv->parent;
+    }
+    if (prevdv)
+    {
+        prevdv->parent = mDNSNULL;
+        FreeDNSSECVerifier(m, prevdv);
+    }
+    // For Optional DNSSEC, we are opportunistically verifying dnssec. We don't care
+    // if something results in bogus as we still want to deliver results to the
+    // application e.g., CNAME processing results in bogus because the path is broken,
+    // but we still want to follow CNAMEs so that we can deliver the final results to
+    // the application.
+    if (dv->ValidationRequired == DNSSEC_VALIDATION_SECURE_OPTIONAL)
+    {
+        LogDNSSEC("StartInsecureProof: Aborting insecure proof for %##s (%s)", dv->q.qname.c, DNSTypeName(dv->q.qtype));
+        dv->DVCallback(m, dv, DNSSEC_Bogus);
+        return;
+    }
+
+    LogDNSSEC("StartInsecureProof for %##s (%s)", dv->q.qname.c, DNSTypeName(dv->q.qtype));
+    // Don't start the insecure proof again after we finish the one that we start here by
+    // setting InsecureProofDone.
+    dv->InsecureProofDone = 1;
+    ProveInsecure(m, dv, mDNSNULL, &trigger);
+    return;
 }
 
 mDNSexport void ValidateWithNSECS(mDNS *const m, DNSSECVerifier *dv, CacheRecord *cr)
 {
     LogDNSSEC("ValidateWithNSECS: called for %s", CRDisplayString(m, cr));
 }
 
 mDNSexport void ValidateWithNSECS(mDNS *const m, DNSSECVerifier *dv, CacheRecord *cr)
 {
     LogDNSSEC("ValidateWithNSECS: called for %s", CRDisplayString(m, cr));
-    // "parent" is set when we are validating a NSEC. In the process of validating that
-    // nsec, we encountered another NSEC. For example, we are looking up the A record for
-    // www.example.com, we got an NSEC at some stage. We come here to validate the NSEC
-    // the first time. While validating the NSEC we remember the original validation result
-    // in the parent. But while validating the NSEC, we got another NSEC back e.g., not
-    // a secure delegation i.e., we got an NSEC proving that DS does not exist. We prove
-    // that again. But if we receive more NSECs after this, we stop.
+
+    // If we are encountering a break in the chain of trust i.e., NSEC/NSEC3s for
+    // DS query, then do the insecure proof. This is important because if we
+    // validate these NSECs normally and prove that they are "secure", we will
+    // end up delivering the secure result to the original question where as
+    // these NSEC/NSEC3s actually prove that DS does not exist and hence insecure.
     //
     //
-    if (dv->parent)
+    // This break in the chain can happen after we have partially validated the
+    // path (dv->ac is non-NULL) or the first time (dv->ac is NULL) after we
+    // fetched the DNSKEY (dv->key is non-NULL). We don't want to do this
+    // if we have just started the non-existence proof (dv->key is NULL) as
+    // it does not indicate a break in the chain of trust.
+    //
+    // If we are already doing a insecurity proof, don't start another one. In
+    // the case of NSECs, it is possible that insecurity proof starts and it
+    // gets NSECs and as part of validating that we receive more NSECS in which
+    // case we don't want to start another insecurity proof.
+    if (dv->ValidationRequired != DNSSEC_VALIDATION_INSECURE &&
+        (!dv->parent || dv->parent->ValidationRequired != DNSSEC_VALIDATION_INSECURE))
     {
     {
-        if (dv->parent->parent)
+         if ((dv->ac && dv->q.qtype == kDNSType_DS) ||
+             (!dv->ac && dv->key && dv->q.qtype == kDNSType_DS))
         {
         {
-            LogMsg("ValidateWithNSECS: ERROR!! dv parent is set already");
-            dv->DVCallback(m, dv, DNSSEC_Indeterminate);
+            LogDNSSEC("ValidateWithNSECS: Starting insecure proof: name %##s ac %p, key %p, parent %p", dv->q.qname.c,
+                dv->ac, dv->key, dv->parent);
+            StartInsecureProof(m, dv);
             return;
         }
             return;
         }
-        else
-        {
-            DNSSECVerifier *pdv = dv;
-            dv = AllocateDNSSECVerifier(m, &pdv->q.qname, pdv->q.qtype, pdv->q.InterfaceID, VerifyNSECCallback, mDNSNULL);
-            if (!dv)
-            {
-                LogMsg("VerifyNSEC: mDNSPlatformMemAlloc failed");
-                pdv->DVCallback(m, pdv, DNSSEC_Indeterminate);
-                return;
-            }
-            LogDNSSEC("ValidateWithNSECS: Parent set, Verifying dv %p %##s (%s)", dv, pdv->q.qname.c, DNSTypeName(pdv->q.qtype));
-            dv->parent = pdv;
-        }
+    }
+    // "parent" is set when we are validating a NSEC and we should not be here in
+    // the normal case when parent is set. For example, we are looking up the A
+    // record for www.example.com and following can happen.
+    //
+    // a) Record does not exist and we get a NSEC
+    // b) While validating (a), we get an NSEC for the first DS record that we look up
+    // c) Record exists but we get NSECs for the first DS record
+    // d) We are able to partially validate (a) or (b), but we get NSECs somewhere in
+    //    the chain
+    //
+    // For (a), parent is not set as we are not validating the NSEC yet. Hence we would
+    // start the validation now.
+    //
+    // For (b), the parent is set, but should be caught by the above "if" block because we 
+    // should have gotten the DNSKEY at least. In the case of nested insecurity proof,
+    // we would end up here and fail with bogus.
+    //
+    // For (c), the parent is not set and should be caught by the above "if" block because we 
+    // should have gotten the DNSKEY at least.
+    //
+    // For (d), the above "if" block would catch it as "dv->ac" is non-NULL.
+    // 
+    // Hence, we should not come here in the normal case. Possible pathological cases are:
+    // Insecure proof getting NSECs while validating NSECs, getting NSECs for DNSKEY for (c)
+    // above etc.
+    if (dv->parent)
+    {
+        LogDNSSEC("ValidateWithNSECS: dv parent set for %##s (%s)", dv->q.qname.c, DNSTypeName(dv->q.qtype));
+        dv->DVCallback(m, dv, DNSSEC_Bogus);
+        return;
     }
     if (cr->resrec.RecordType == kDNSRecordTypePacketNegative)
     {
     }
     if (cr->resrec.RecordType == kDNSRecordTypePacketNegative)
     {
+        mDNSu8 rcode;
         CacheRecord *neg = cr->nsec;
         CacheRecord *neg = cr->nsec;
+        mDNSBool nsecs_seen = mDNSfalse;
+
         while (neg)
         {
         while (neg)
         {
+            // The list can only have NSEC or NSEC3s. This was checked when we added the
+            // NSECs to the cache record.
+            if (neg->resrec.rrtype == kDNSType_NSEC)
+                nsecs_seen = mDNStrue;
             LogDNSSEC("ValidateWithNSECS: NSECCached Record %s", CRDisplayString(m, neg));
             neg = neg->next;
         }
 
             LogDNSSEC("ValidateWithNSECS: NSECCached Record %s", CRDisplayString(m, neg));
             neg = neg->next;
         }
 
-        if (cr->rcode == kDNSFlag1_RC_NoErr)
+        rcode = (mDNSu8)(cr->responseFlags.b[1] & kDNSFlag1_RC_Mask);
+        if (rcode == kDNSFlag1_RC_NoErr)
         {
         {
-            NoDataProof(m, dv, cr);
+            if (nsecs_seen)
+                NoDataProof(m, dv, cr);
+            else
+                NSEC3NoDataProof(m, dv, cr);
         }
         }
-        else if (cr->rcode == kDNSFlag1_RC_NXDomain)
+        else if (rcode == kDNSFlag1_RC_NXDomain)
         {
         {
-            NameErrorProof(m, dv, cr);
+            if (nsecs_seen)
+                NameErrorProof(m, dv, cr);
+            else
+                NSEC3NameErrorProof(m, dv, cr);
         }
         else
         {
         }
         else
         {
-            LogDNSSEC("ValidateWithNSECS: Rcode %d invalid", cr->rcode);
-            dv->DVCallback(m, dv, DNSSEC_Insecure);
+            LogDNSSEC("ValidateWithNSECS: Rcode %d invalid", rcode);
+            dv->DVCallback(m, dv, DNSSEC_Bogus);
         }
     }
     else
     {
         LogMsg("ValidateWithNSECS: Not a valid cache record %s for NSEC proofs", CRDisplayString(m, cr));
         }
     }
     else
     {
         LogMsg("ValidateWithNSECS: Not a valid cache record %s for NSEC proofs", CRDisplayString(m, cr));
-        dv->DVCallback(m, dv, DNSSEC_Insecure);
+        dv->DVCallback(m, dv, DNSSEC_Bogus);
         return;
     }
 }
         return;
     }
 }
+
+#else // !DNSSEC_DISABLED
+
+mDNSexport mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, CacheRecord *negcr, mDNSu8 rcode)
+{
+    (void)m;
+    (void)crlist;
+    (void)negcr;
+    (void)rcode;
+
+    return mDNSfalse;
+}
+
+#endif // !DNSSEC_DISABLED
index b85e10360b1fdbb06ac799470b209da4cddf11f5..3dbb841f1577055012dc9ccea0923bb4158b3182 100644 (file)
 #include "dnssec.h"
 
 extern mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, CacheRecord *negcr, mDNSu8 rcode);
 #include "dnssec.h"
 
 extern mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, CacheRecord *negcr, mDNSu8 rcode);
-extern mDNSBool NSECAnswersENT(const ResourceRecord *const rr, domainname *name);
 extern void WildcardAnswerProof(mDNS *const m, DNSSECVerifier *dv);
 extern void ValidateWithNSECS(mDNS *const m, DNSSECVerifier *dv, CacheRecord *rr);
 extern void WildcardAnswerProof(mDNS *const m, DNSSECVerifier *dv);
 extern void ValidateWithNSECS(mDNS *const m, DNSSECVerifier *dv, CacheRecord *rr);
-mDNSexport mDNSBool NSECAnswersDS(mDNS *const m, ResourceRecord *rr, DNSQuestion *q);
+extern mDNSBool NSECAnswersDS(mDNS *const m, ResourceRecord *rr, DNSQuestion *q);
+extern int CountLabelsMatch(const domainname *const d1, const domainname *const d2);
+extern void NameErrorNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status);
+extern void VerifyNSEC(mDNS *const m, ResourceRecord *rr, RRVerifier *rv, DNSSECVerifier *pdv, CacheRecord *ncr,
+       DNSSECVerifierCallback callback);
+extern CacheRecord *NSECRecordIsDelegation(mDNS *const m, domainname *name, mDNSu16 qtype);
+extern void NoDataNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status);
 
 #endif // __NSEC_H
 
 #endif // __NSEC_H
diff --git a/mDNSCore/nsec3.c b/mDNSCore/nsec3.c
new file mode 100644 (file)
index 0000000..a039418
--- /dev/null
@@ -0,0 +1,769 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2011 Apple Computer, 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.
+ */
+
+// ***************************************************************************
+// nsec3.c: This file contains support functions to validate NSEC3 records for
+// NODATA and NXDOMAIN error.
+// ***************************************************************************
+
+#include "mDNSEmbeddedAPI.h"
+#include "DNSCommon.h"
+#include "CryptoAlg.h"
+#include "nsec3.h"
+#include "nsec.h"
+
+// Define DNSSEC_DISABLED to remove all the DNSSEC functionality
+// and use the stub functions implemented later in this file.
+
+#ifndef DNSSEC_DISABLED
+
+typedef enum 
+{
+    NSEC3ClosestEncloser,
+    NSEC3Covers,
+    NSEC3CEProof
+} NSEC3FindValues;
+
+//#define NSEC3_DEBUG 1
+
+#if NSEC3_DEBUG
+mDNSlocal void PrintHash(mDNSu8 *digest, int digestlen, char *buffer, int buflen)
+{
+    int length = 0;
+    for (int j = 0; j < digestlen; j++)
+    {
+        length += mDNS_snprintf(buffer+length, buflen-length-1, "%x", digest[j]);
+    }
+}
+#endif
+
+mDNSlocal mDNSBool NSEC3OptOut(CacheRecord *cr)
+{
+    const RDataBody2 *const rdb = (RDataBody2 *)cr->resrec.rdata->u.data;
+    rdataNSEC3 *nsec3 = (rdataNSEC3 *)rdb->data;
+    return (nsec3->flags & NSEC3_FLAGS_OPTOUT);
+}
+
+mDNSlocal int NSEC3SameName(const mDNSu8 *name, int namelen, const mDNSu8 *nsecName, int nsecLen)
+{
+    int i;
+
+    // Note: With NSEC3, the lengths should always be same. 
+    if (namelen != nsecLen)
+    {
+        LogMsg("NSEC3SameName: ERROR!! namelen %d, nsecLen %d", namelen, nsecLen);
+        return ((namelen < nsecLen) ? -1 : 1);
+    }
+
+    for (i = 0; i < namelen; i++)
+    {
+        mDNSu8 ac = *name++;
+        mDNSu8 bc = *nsecName++;
+        if (mDNSIsUpperCase(ac)) ac += 'a' - 'A';
+        if (mDNSIsUpperCase(bc)) bc += 'a' - 'A';
+        if (ac != bc)
+        {
+            verbosedebugf("NSEC3SameName: returning ac %c, bc %c", ac, bc);
+            return ((ac < bc) ? -1 : 1);
+        }
+    }
+    return 0;
+}
+
+// Does the NSEC3 in "ncr" covers the "name" ?
+// hashName is hash of the "name" and b32Name is the base32 encoded equivalent.
+mDNSlocal mDNSBool NSEC3CoversName(mDNS *const m, CacheRecord *ncr, const mDNSu8 *hashName, int hashLen, const mDNSu8 *b32Name,
+       int b32len)
+{
+    mDNSu8 *nxtName;
+    int nxtLength;
+    int ret, ret1, ret2;
+    const mDNSu8 b32nxtname[NSEC3_MAX_B32_LEN+1];
+    int b32nxtlen;
+
+    NSEC3Parse(&ncr->resrec, mDNSNULL, &nxtLength, &nxtName, mDNSNULL, mDNSNULL);
+
+    if (nxtLength != hashLen || ncr->resrec.name->c[0] != b32len)
+        return mDNSfalse;
+
+    // Compare the owner names and the "nxt" names.
+    //
+    // Owner name is base32 encoded and hence use the base32 encoded name b32name.
+    // nxt name is binary and hence use the binary value in hashName. 
+    ret1 = NSEC3SameName(&ncr->resrec.name->c[1], ncr->resrec.name->c[0], b32Name, b32len);
+    ret2 = DNSMemCmp(nxtName, hashName, hashLen);
+
+#if NSEC3_DEBUG
+    {
+        char nxtbuf1[50];
+        char nxtbuf2[50];
+
+        PrintHash(nxtName, nxtLength, nxtbuf1, sizeof(nxtbuf1));
+        PrintHash((mDNSu8 *)hashName, hashLen, nxtbuf2, sizeof(nxtbuf2));
+        LogMsg("NSEC3CoversName: Owner name %s, name %s", &ncr->resrec.name->c[1], b32Name);
+        LogMsg("NSEC3CoversName: Nxt hash name %s, name %s", nxtbuf1, nxtbuf2);
+    }
+#endif
+
+    // "name" is greater than the owner name and smaller than nxtName. This also implies
+       // that nxtName > owner name implying that it is normal NSEC3.
+    if (ret1 < 0 && ret2 > 0)
+    {
+        LogDNSSEC("NSEC3CoversName: NSEC3 %s covers %s (Normal)", CRDisplayString(m, ncr), b32Name);
+        return mDNStrue;
+    }
+    // Need to compare the owner name and "nxt" to see if this is the last
+    // NSEC3 in the zone. Only the owner name is in base32 and hence we need to
+    // convert the nxtName to base32.
+    b32nxtlen = baseEncode((char *)b32nxtname, sizeof(b32nxtname), nxtName, nxtLength, ENC_BASE32);
+    if (!b32nxtlen)
+    {
+        LogDNSSEC("NSEC3CoversName: baseEncode of nxtName of %s failed", CRDisplayString(m, ncr));
+        return mDNSfalse;
+    }
+    if (b32len != b32nxtlen)
+    {
+        LogDNSSEC("NSEC3CoversName: baseEncode of nxtName for %s resulted in wrong length b32nxtlen %d, b32len %d",
+            CRDisplayString(m, ncr), b32len, b32nxtlen);
+        return mDNSfalse;
+    }
+    LogDNSSEC("NSEC3CoversName: Owner name %s, b32nxtname %s, ret1 %d, ret2 %d", &ncr->resrec.name->c[1], b32nxtname, ret1, ret2);
+
+    // If it is the last NSEC3 in the zone nxt < "name" and NSEC3SameName returns -1.
+    //
+    // - ret1 < 0 means "name > owner"
+    // - ret2 > 0 means "name < nxt"
+    // 
+    // Note: We also handle the case of only NSEC3 in the zone where NSEC3SameName returns zero.
+    ret = NSEC3SameName(b32nxtname, b32nxtlen, &ncr->resrec.name->c[1], ncr->resrec.name->c[0]);
+    if (ret <= 0 &&
+        (ret1 < 0 || ret2 > 0))
+    {
+        LogDNSSEC("NSEC3CoversName: NSEC3 %s covers %s (Last), ret1 %d, ret2 %d", CRDisplayString(m, ncr), b32Name, ret1, ret2);
+        return mDNStrue;
+    }
+
+    return mDNSfalse;
+}
+
+// This function can be called with NSEC3ClosestEncloser, NSEC3Covers and NSEC3CEProof
+//
+// Passing in NSEC3ClosestEncloser means "find an exact match for the origName".
+// Passing in NSEC3Covers means "find an NSEC3 that covers the origName".
+//
+// i.e., in both cases the nsec3 records are iterated to find the best match and returned.
+// With NSEC3ClosestEncloser, as we are just looking for a name match, extra checks for
+// the types being present or absent will not be checked.
+//
+// If NSEC3CEProof is passed, the name is tried as such first by iterating through all NSEC3s
+// finding a ClosestEncloser or CloserEncloser and then one label skipped from the left and
+// retried again till both the closest and closer encloser is found.
+//
+// ncr is the negative cache record that has the NSEC3 chain
+// origName is the name for which we are trying to find the ClosestEncloser etc.
+// closestEncloser and closerEncloser are the return values of the function
+// ce is the closest encloser that will be returned if we find one
+mDNSlocal mDNSBool NSEC3Find(mDNS *const m, NSEC3FindValues val, CacheRecord *ncr, domainname *origName, CacheRecord **closestEncloser,
+       CacheRecord **closerEncloser, const domainname **ce, mDNSu16 qtype)
+{
+    int i;
+    int labelCount = CountLabels(origName);
+    CacheRecord *cr;
+    rdataNSEC3 *nsec3;
+
+    (void) qtype; // unused
+    // Pick the first NSEC for the iterations, salt etc.
+    for (cr = ncr->nsec; cr; cr = cr->next)
+    {
+        if (cr->resrec.rrtype == kDNSType_NSEC3)
+        {
+            const RDataBody2 *const rdb = (RDataBody2 *)cr->resrec.rdata->u.data;
+            nsec3 = (rdataNSEC3 *)rdb->data;
+            break;
+        }
+    }
+    if (!cr)
+    {
+        LogMsg("NSEC3Find: cr NULL");
+        return mDNSfalse;
+    }
+
+    // Note: The steps defined in this function are for "NSEC3CEProof". As part of NSEC3CEProof,
+    // we need to find both the closestEncloser and closerEncloser which can also be found
+    // by passing NSEC3ClosestEncloser and NSEC3Covers respectively.
+    //
+    // Section 8.3 of RFC 5155.
+    // 1.  Set SNAME=QNAME.  Clear the flag.
+    //
+    // closerEncloser is the "flag". "name" below is SNAME.
+
+    if (closestEncloser)
+    {
+        *ce = mDNSNULL;
+        *closestEncloser = mDNSNULL;
+    }
+    if (closerEncloser)
+        *closerEncloser = mDNSNULL;
+
+    // If we are looking for a closestEncloser or a covering NSEC3, we don't have
+    // to truncate the name. For the give name, try to find the closest or closer
+    // encloser.
+    if (val != NSEC3CEProof)
+    {
+        labelCount = 0;
+    }
+
+    for (i = 0; i < labelCount + 1; i++)
+    { 
+        int hlen;
+        const mDNSu8 hashName[NSEC3_MAX_HASH_LEN];
+        const domainname *name;
+        const mDNSu8 b32Name[NSEC3_MAX_B32_LEN+1];
+        int b32len;
+
+        name = SkipLeadingLabels(origName, i);
+        if (!NSEC3HashName(name, nsec3, mDNSNULL, 0, hashName, &hlen))
+        {
+            LogMsg("NSEC3Find: NSEC3HashName failed for ##s", name->c);
+            continue;
+        }
+
+        b32len = baseEncode((char *)b32Name, sizeof(b32Name), (mDNSu8 *)hashName, hlen, ENC_BASE32);
+        if (!b32len)
+        {
+            LogMsg("NSEC3Find: baseEncode of name %##s failed", name->c);
+            continue;
+        }
+
+
+        for (cr = ncr->nsec; cr; cr = cr->next)
+        {
+            const domainname *nsecZone;
+            int result, subdomain;
+
+            if (cr->resrec.rrtype != kDNSType_NSEC3)
+                continue;
+
+            nsecZone = SkipLeadingLabels(cr->resrec.name, 1);
+            if (!nsecZone)
+            {
+                LogMsg("NSEC3Find: SkipLeadingLabel failed for %s, current name %##s",
+                    CRDisplayString(m, cr), name->c);
+                continue;
+            }
+
+            // NSEC3 owner names are formed by hashing the owner name and then appending
+            // the zone name to it. If we skip the first label, the rest should be
+            // the zone name. See whether it is the subdomain of the name we are looking
+            // for. 
+            result = DNSSECCanonicalOrder(origName, nsecZone, &subdomain);
+            
+            // The check can't be a strict subdomain check. When NSEC3ClosestEncloser is
+            // passed in, there can be an exact match. If it is a subdomain or an exact
+            // match, we should continue with the proof.
+            if (!(subdomain || !result))
+            {
+                LogMsg("NSEC3Find: NSEC3 %s not a subdomain of %##s, result %d", CRDisplayString(m, cr),
+                    origName->c, result);
+                continue;
+            }
+
+            // 2.1) If there is no NSEC3 RR in the response that matches SNAME
+            // (i.e., an NSEC3 RR whose owner name is the same as the hash of
+            // SNAME, prepended as a single label to the zone name), clear
+            // the flag.
+            //
+            // Note: We don't try to determine the actual zone name. We know that
+            // the labels following the hash (nsecZone) is the ancestor and we don't
+            // know where the zone cut is. Hence, we verify just the hash to be
+            // the same.
+
+            if (val == NSEC3ClosestEncloser || val == NSEC3CEProof)
+            {
+                if (!NSEC3SameName(&cr->resrec.name->c[1], cr->resrec.name->c[0], (const mDNSu8 *)b32Name, b32len))
+                {
+                    int bmaplen;
+                    mDNSu8 *bmap;
+
+                    // For NSEC3ClosestEncloser, we are finding an exact match and
+                    // "type" specific checks should be done by the caller.
+                    if (val != NSEC3ClosestEncloser)
+                    {
+                        // DNAME bit must not be set and NS bit may be set only if SOA bit is set
+                        NSEC3Parse(&cr->resrec, mDNSNULL, mDNSNULL, mDNSNULL, &bmaplen, &bmap);
+                        if (BitmapTypeCheck(bmap, bmaplen, kDNSType_DNAME))
+                        {
+                            LogDNSSEC("NSEC3Find: DNAME bit set in %s, ignoring", CRDisplayString(m, cr));
+                            return mDNSfalse;
+                        }
+                        // This is the closest encloser and should come from the right zone.
+                        if (BitmapTypeCheck(bmap, bmaplen, kDNSType_NS) &&
+                            !BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA))
+                        {
+                            LogDNSSEC("NSEC3Find: NS bit set without SOA bit in %s, ignoring", CRDisplayString(m, cr));
+                            return mDNSfalse;
+                        }
+                    }
+
+                    LogDNSSEC("NSEC3Find: ClosestEncloser %s found for name %##s", CRDisplayString(m, cr), name->c);
+                    if (closestEncloser)
+                    {
+                        *ce = name;
+                        *closestEncloser = cr;
+                    }
+                    if (val == NSEC3ClosestEncloser)
+                        return mDNStrue;
+                    else
+                        break;
+                }
+            }
+
+            if ((val == NSEC3Covers || val == NSEC3CEProof) && !(*closerEncloser))
+            {
+                if (NSEC3CoversName(m, cr, hashName, hlen, b32Name, b32len))
+                {
+                    // 2.2) If there is an NSEC3 RR in the response that covers SNAME, set the flag.
+                    if (closerEncloser)
+                        *closerEncloser = cr;
+                    if (val == NSEC3Covers)
+                        return mDNStrue;
+                    else
+                        break;
+                }
+            }
+        }
+        // 2.3) If there is a matching NSEC3 RR in the response and the flag
+        // was set, then the proof is complete, and SNAME is the closest
+        // encloser.
+        if (val == NSEC3CEProof)
+        {
+            if (*closestEncloser && *closerEncloser)
+            {
+                LogDNSSEC("NSEC3Find: Found closest and closer encloser");
+                return mDNStrue;
+            }
+
+            // 2.4) If there is a matching NSEC3 RR in the response, but the flag
+            // is not set, then the response is bogus.
+            //
+            // Note: We don't have to wait till we finish trying all the names. If the matchName
+            // happens, we found the closest encloser which means we should have found the closer
+            // encloser before.
+
+            if (*closestEncloser && !(*closerEncloser))
+            {
+                LogDNSSEC("NSEC3Find: Found closest, but not closer encloser");
+                return mDNSfalse;
+            }
+        }
+        // 3.  Truncate SNAME by one label from the left, go to step 2.
+    }
+    LogDNSSEC("NSEC3Find: Cannot find name %##s (%s)", origName->c, DNSTypeName(qtype));
+    return mDNSfalse;
+}
+
+mDNSlocal mDNSBool NSEC3ClosestEncloserProof(mDNS *const m, CacheRecord *ncr, domainname *name, CacheRecord **closestEncloser, CacheRecord **closerEncloser,
+       const domainname **ce, mDNSu16 qtype)
+{
+    if (!NSEC3Find(m, NSEC3CEProof, ncr, name, closestEncloser, closerEncloser, ce, qtype))
+    {
+        LogDNSSEC("NSEC3ClosestEncloserProof: ERROR!! Cannot do closest encloser proof");
+        return mDNSfalse;
+    }
+
+    // Note: It is possible that closestEncloser and closerEncloser are the same.
+    if (!closestEncloser || !closerEncloser || !ce)
+    {
+        LogMsg("NSEC3ClosestEncloserProof: ClosestEncloser %p or CloserEncloser %p ce %p, something is NULL", *closestEncloser,
+            *closerEncloser, *ce);
+        return mDNSfalse;
+    }
+
+    // If the name exists, we should not have gotten the name error
+    if (SameDomainName((*ce), name))
+    {
+        LogMsg("NSEC3ClosestEncloserProof: ClosestEncloser %s same as origName %##s", CRDisplayString(m, *closestEncloser),
+            (*ce)->c);
+        return mDNSfalse;
+    }
+    return mDNStrue;
+}
+
+mDNSlocal mDNSBool VerifyNSEC3(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr, CacheRecord *closestEncloser,
+    CacheRecord *closerEncloser, CacheRecord *wildcard, DNSSECVerifierCallback callback)
+{
+    mStatus status;
+    RRVerifier *r;
+
+    // We have three NSEC3s. If any of two are same, we should just prove one of them.
+    // This is just not an optimization; DNSSECNegativeValidationCB does not handle
+    // identical NSEC3s very well.
+
+    if (closestEncloser == closerEncloser)
+        closestEncloser = mDNSNULL;
+    if (closerEncloser == wildcard)
+        closerEncloser = mDNSNULL;
+    if (closestEncloser == wildcard)
+        closestEncloser = mDNSNULL;
+
+    dv->pendingNSEC = mDNSNULL;
+    if (closestEncloser)
+    {
+        r = AllocateRRVerifier(&closestEncloser->resrec, &status);
+        if (!r)
+            return mDNSfalse;
+        r->next = dv->pendingNSEC;
+        dv->pendingNSEC = r;
+    }
+    if (closerEncloser)
+    {
+        r = AllocateRRVerifier(&closerEncloser->resrec, &status);
+        if (!r)
+            return mDNSfalse;
+        r->next = dv->pendingNSEC;
+        dv->pendingNSEC = r;
+    }
+    if (wildcard)
+    {
+        r = AllocateRRVerifier(&wildcard->resrec, &status);
+        if (!r)
+            return mDNSfalse;
+        r->next = dv->pendingNSEC;
+        dv->pendingNSEC = r;
+    }
+    if (!dv->pendingNSEC)
+    {
+        LogMsg("VerifyNSEC3: ERROR!! pending NSEC null");
+        return mDNSfalse;
+    }
+    r = dv->pendingNSEC;
+    dv->pendingNSEC = r->next;
+    r->next = mDNSNULL;
+
+    LogDNSSEC("VerifyNSEC3: Verifying %##s (%s)", r->name.c, DNSTypeName(r->rrtype));
+    if (!dv->pendingNSEC)
+        VerifyNSEC(m, mDNSNULL, r, dv, ncr, mDNSNULL);
+    else
+        VerifyNSEC(m, mDNSNULL, r, dv, ncr, callback);
+    return mDNStrue;
+}
+
+mDNSexport void NSEC3NameErrorProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr)
+{
+    CacheRecord *closerEncloser;
+    CacheRecord *closestEncloser;
+    CacheRecord *wildcard;
+    const domainname *ce = mDNSNULL;
+    domainname wild;
+
+    if (!NSEC3ClosestEncloserProof(m, ncr, &dv->q.qname, &closestEncloser, &closerEncloser, &ce, dv->q.qtype))
+    {
+        goto error;
+    }
+    LogDNSSEC("NSEC3NameErrorProof: ClosestEncloser %s, ce %##s", CRDisplayString(m, closestEncloser), ce->c);
+    LogDNSSEC("NSEC3NameErrorProof: CloserEncloser %s", CRDisplayString(m, closerEncloser));
+
+    // *.closestEncloser should be covered by some nsec3 which would then prove
+    // that the wildcard does not exist
+    wild.c[0] = 1;
+    wild.c[1] = '*';
+    wild.c[2] = 0;
+    if (!AppendDomainName(&wild, ce))
+    {
+        LogMsg("NSEC3NameErrorProof: Can't append domainname to closest encloser name %##s", ce->c);
+        goto error;
+    }
+    if (!NSEC3Find(m, NSEC3Covers, ncr, &wild, mDNSNULL, &wildcard, mDNSNULL, dv->q.qtype))
+    {
+        LogMsg("NSEC3NameErrorProof: Cannot find encloser for wildcard");
+        goto error;
+    }
+    else
+    {
+        LogDNSSEC("NSEC3NameErrorProof: Wildcard %##s covered by %s", wild.c, CRDisplayString(m, wildcard));
+        if (wildcard == closestEncloser)
+        {
+            LogDNSSEC("NSEC3NameErrorProof: ClosestEncloser matching Wildcard %s", CRDisplayString(m, wildcard));
+        }
+    }
+    if (NSEC3OptOut(closerEncloser))
+    {
+        dv->flags |= NSEC3_OPT_OUT;
+    }
+    if (!VerifyNSEC3(m, dv, ncr, closestEncloser, closerEncloser, wildcard, NameErrorNSECCallback))
+        goto error;
+    else
+        return;
+
+error:
+    dv->DVCallback(m, dv, DNSSEC_Bogus);
+}
+
+// Section 8.5, 8.6 of RFC 5155 first paragraph
+mDNSlocal mDNSBool NSEC3NoDataError(mDNS *const m, CacheRecord *ncr, domainname *name, mDNSu16 qtype, CacheRecord **closestEncloser)
+{
+    const domainname *ce = mDNSNULL;
+
+    *closestEncloser = mDNSNULL;
+    // Note: This also covers ENT in which case the bitmap is empty
+    if (NSEC3Find(m, NSEC3ClosestEncloser, ncr, name, closestEncloser, mDNSNULL, &ce, qtype))
+    {
+        int bmaplen;
+        mDNSu8 *bmap;
+        mDNSBool ns, soa;
+
+        NSEC3Parse(&(*closestEncloser)->resrec, mDNSNULL, mDNSNULL, mDNSNULL, &bmaplen, &bmap);
+        if (BitmapTypeCheck(bmap, bmaplen, qtype) || BitmapTypeCheck(bmap, bmaplen, kDNSType_CNAME))
+        {
+            LogMsg("NSEC3NoDataError: qtype %s exists in %s", DNSTypeName(qtype), CRDisplayString(m, *closestEncloser));
+            return mDNSfalse;
+        }
+        ns = BitmapTypeCheck(bmap, bmaplen, kDNSType_NS);
+        soa = BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA);
+        if (qtype != kDNSType_DS)
+        {
+            // For non-DS type questions, we don't want to use the parent side records to
+            // answer it
+            if (ns && !soa)
+            {
+                LogDNSSEC("NSEC3NoDataError: Parent side NSEC %s, can't use for child qname %##s (%s)",
+                    CRDisplayString(m, *closestEncloser), name->c, DNSTypeName(qtype));
+                return mDNSfalse;
+            }
+        }
+        else
+        {
+            if (soa)
+            {
+                LogDNSSEC("NSEC3NoDataError: Child side NSEC %s, can't use for parent qname %##s (%s)",
+                    CRDisplayString(m, *closestEncloser), name->c, DNSTypeName(qtype));
+                return mDNSfalse;
+            }
+        }
+        LogDNSSEC("NSEC3NoDataError: Name -%##s- exists, but qtype %s does not exist in %s", name->c, DNSTypeName(qtype), CRDisplayString(m, *closestEncloser));
+        return mDNStrue;
+    }
+    return mDNSfalse;
+}
+
+mDNSexport void NSEC3NoDataProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr)
+{
+    CacheRecord *closerEncloser = mDNSNULL;
+    CacheRecord *closestEncloser = mDNSNULL;
+    CacheRecord *wildcard = mDNSNULL;
+    const domainname *ce = mDNSNULL;
+    domainname wild;
+
+    // Section 8.5, 8.6 of RFC 5155
+    if (NSEC3NoDataError(m, ncr, &dv->q.qname, dv->q.qtype, &closestEncloser))
+    {
+        goto verify;
+    }
+    // Section 8.6, 8.7: if we can't find the NSEC3 RR, verify the closest encloser proof
+    // for QNAME and the "next closer" should have the opt out
+    if (!NSEC3ClosestEncloserProof(m, ncr, &dv->q.qname, &closestEncloser, &closerEncloser, &ce, dv->q.qtype))
+    {
+        goto error;
+    }
+
+    // Section 8.7: find a matching NSEC3 for *.closestEncloser
+    wild.c[0] = 1;
+    wild.c[1] = '*';
+    wild.c[2] = 0;
+    if (!AppendDomainName(&wild, ce))
+    {
+        LogMsg("NSEC3NameErrorProof: Can't append domainname to closest encloser name %##s", ce->c);
+        goto error;
+    }
+    if (!NSEC3Find(m, NSEC3ClosestEncloser, ncr, &wild, &wildcard, mDNSNULL, &ce, dv->q.qtype))
+    {
+        // Not a wild card case. Section 8.6 second para applies.
+        LogDNSSEC("NSEC3NoDataProof: Cannot find encloser for wildcard, perhaps not a wildcard case");
+        if (!NSEC3OptOut(closerEncloser))
+        {
+            LogDNSSEC("NSEC3DataProof: opt out not set for %##s (%s), bogus", dv->q.qname.c, DNSTypeName(dv->q.qtype));
+            goto error;
+        }
+        LogDNSSEC("NSEC3DataProof: opt out set, proof complete for %##s (%s)", dv->q.qname.c, DNSTypeName(dv->q.qtype));
+        dv->flags |= NSEC3_OPT_OUT;
+    }
+    else
+    {
+        int bmaplen;
+        mDNSu8 *bmap;
+        NSEC3Parse(&wildcard->resrec, mDNSNULL, mDNSNULL, mDNSNULL, &bmaplen, &bmap);
+        if (BitmapTypeCheck(bmap, bmaplen, dv->q.qtype) || BitmapTypeCheck(bmap, bmaplen, kDNSType_CNAME))
+        {
+            LogDNSSEC("NSEC3NoDataProof: qtype %s exists in %s", DNSTypeName(dv->q.qtype), CRDisplayString(m, wildcard));
+            goto error;
+        }
+        if (dv->q.qtype == kDNSType_DS && BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA))
+        {
+            LogDNSSEC("NSEC3NoDataProof: Child side wildcard NSEC3 %s, can't use for parent qname %##s (%s)",
+                CRDisplayString(m, wildcard), dv->q.qname.c, DNSTypeName(dv->q.qtype));
+            goto error;
+        }
+        else if (dv->q.qtype != kDNSType_DS && !BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA) &&
+            BitmapTypeCheck(bmap, bmaplen, kDNSType_NS))
+        {
+            // Don't use the parent side record for this
+            LogDNSSEC("NSEC3NoDataProof: Parent side wildcard NSEC3 %s, can't use for child qname %##s (%s)",
+                CRDisplayString(m, wildcard), dv->q.qname.c, DNSTypeName(dv->q.qtype));
+            goto error;
+        }
+        LogDNSSEC("NSEC3NoDataProof: Wildcard %##s matched by %s", wild.c, CRDisplayString(m, wildcard));
+    }
+verify:
+
+    if (!VerifyNSEC3(m, dv, ncr, closestEncloser, closerEncloser, wildcard, NoDataNSECCallback))
+        goto error;
+    else
+        return;
+error:
+    dv->DVCallback(m, dv, DNSSEC_Bogus);
+}
+
+mDNSexport mDNSBool NSEC3WildcardAnswerProof(mDNS *const m, CacheRecord *ncr, DNSSECVerifier *dv)
+{
+    int skip;
+    const domainname *nc;
+    CacheRecord *closerEncloser;
+
+    (void) m;
+
+    // Find the next closer name and prove that it is covered by the NSEC3
+    skip = CountLabels(&dv->origName) - CountLabels(dv->wildcardName) - 1;
+    if (skip)
+        nc = SkipLeadingLabels(&dv->origName, skip);
+    else
+        nc = &dv->origName;
+
+    LogDNSSEC("NSEC3WildcardAnswerProof: wildcard name %##s", nc->c);
+
+    if (!NSEC3Find(m, NSEC3Covers, ncr, (domainname *)nc, mDNSNULL, &closerEncloser, mDNSNULL, dv->q.qtype))
+    {
+        LogMsg("NSEC3WildcardAnswerProof: Cannot find closer encloser");
+        return mDNSfalse;
+    }
+    if (!closerEncloser)
+    {
+        LogMsg("NSEC3WildcardAnswerProof: closerEncloser NULL");
+        return mDNSfalse;
+    }
+    if (NSEC3OptOut(closerEncloser))
+    {
+        dv->flags |= NSEC3_OPT_OUT;
+    }
+    // NSEC3 Verification is done by the caller
+    return mDNStrue;
+}
+
+mDNSexport CacheRecord *NSEC3RecordIsDelegation(mDNS *const m, domainname *name, mDNSu16 qtype)
+{
+    CacheGroup *cg;
+    CacheRecord *cr;
+    CacheRecord *ncr;
+    mDNSu32 slot, namehash;
+
+    slot = HashSlot(name);
+    namehash = DomainNameHashValue(name);
+
+    cg = CacheGroupForName(m, (const mDNSu32)slot, namehash, name);
+    if (!cg)
+    {
+        LogDNSSEC("NSEC3RecordForName: cg NULL for %##s", name);
+        return mDNSNULL;
+    }
+    for (ncr = cg->members; ncr; ncr = ncr->next)
+    {
+        if (ncr->resrec.RecordType != kDNSRecordTypePacketNegative ||
+            ncr->resrec.rrtype != qtype)
+        {
+            continue;
+        }
+        for (cr = ncr->nsec; cr; cr = cr->next)
+        {
+            int hlen, b32len;
+            const mDNSu8 hashName[NSEC3_MAX_HASH_LEN];
+            const mDNSu8 b32Name[NSEC3_MAX_B32_LEN+1];
+            const RDataBody2 *const rdb = (RDataBody2 *)cr->resrec.rdata->u.data;
+            rdataNSEC3 *nsec3;
+
+            if (cr->resrec.rrtype != kDNSType_NSEC3)
+                continue;
+
+            nsec3 = (rdataNSEC3 *)rdb->data;
+
+            if (!NSEC3HashName(name, nsec3, mDNSNULL, 0, hashName, &hlen))
+            {
+                LogMsg("NSEC3RecordIsDelegation: NSEC3HashName failed for ##s", name->c);
+                return mDNSNULL;
+            }
+
+            b32len = baseEncode((char *)b32Name, sizeof(b32Name), (mDNSu8 *)hashName, hlen, ENC_BASE32);
+            if (!b32len)
+            {
+                LogMsg("NSEC3RecordIsDelegation: baseEncode of name %##s failed", name->c);
+                return mDNSNULL;
+            }
+            // Section 2.3 of RFC 4035 states that:
+            //
+            // Each owner name in the zone that has authoritative data or a delegation point NS RRset MUST
+            // have an NSEC resource record. 
+            //
+            // This applies to NSEC3 record. So, if we have an NSEC3 record matching the question name with the
+            // NS bit set, then this is a delegation.
+            //
+            if (!NSEC3SameName(&cr->resrec.name->c[1], cr->resrec.name->c[0], (const mDNSu8 *)b32Name, b32len))
+            {
+                int bmaplen;
+                mDNSu8 *bmap;
+                
+                LogDNSSEC("NSEC3RecordIsDelegation: CacheRecord %s matches name %##s, b32name %s", CRDisplayString(m, cr), name->c, b32Name);
+                NSEC3Parse(&cr->resrec, mDNSNULL, mDNSNULL, mDNSNULL, &bmaplen, &bmap);
+
+                // See the Insecure Delegation Proof section in dnssec-bis: DS bit and SOA bit
+                // should be absent
+                if (BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA) ||
+                    BitmapTypeCheck(bmap, bmaplen, kDNSType_DS))
+                {
+                    LogDNSSEC("NSEC3RecordIsDelegation: CacheRecord %s has DS or SOA bit set, ignoring", CRDisplayString(m, cr));
+                    return mDNSNULL;
+                }
+                if (BitmapTypeCheck(bmap, bmaplen, kDNSType_NS))
+                    return cr;
+                else
+                    return mDNSNULL;
+            }
+            // If opt-out is not set, then it does not cover any delegations
+            if (!(nsec3->flags & NSEC3_FLAGS_OPTOUT))
+                continue;
+            // Opt-out allows insecure delegations to exist without the NSEC3 RR at the
+            // hashed owner name (see RFC 5155 section 6.0).
+            if (NSEC3CoversName(m, cr, hashName, hlen, b32Name, b32len))
+            {
+                LogDNSSEC("NSEC3RecordIsDelegation: CacheRecord %s covers name %##s with optout", CRDisplayString(m, cr), name->c);
+                return cr;
+            }
+        }
+    }
+    return mDNSNULL;
+}
+
+#else // !DNSSEC_DISABLED
+
+#endif // !DNSSEC_DISABLED
diff --git a/mDNSCore/nsec3.h b/mDNSCore/nsec3.h
new file mode 100644 (file)
index 0000000..ce3b85a
--- /dev/null
@@ -0,0 +1,28 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2011 Apple Computer, 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 __NSEC3_H
+#define __NSEC3_H
+
+#include "dnssec.h"
+
+extern mDNSBool NSEC3WildcardAnswerProof(mDNS *const m, CacheRecord *ncr, DNSSECVerifier *dv);
+extern void NSEC3NameErrorProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr);
+extern void NSEC3NoDataProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr);
+extern CacheRecord *NSEC3RecordIsDelegation(mDNS *const m, domainname *name, mDNSu16 qtype);
+
+#endif // __NSEC3_H
index 040a1c9cf32761a07c6c651084951493a1703035..be7a5a3266366adb77754f344203125d6f15c9d5 100755 (executable)
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 4 -*-
  *
 /* -*- Mode: C; tab-width: 4 -*-
  *
- * Copyright (c) 2002-2012 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2002-2013 Apple Computer, 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.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -57,6 +57,8 @@ mDNSu8 NumUnicastDNSServers = 0;
             (m)->NextuDNSEvent = ((rr)->LastAPTime + (rr)->ThisAPInterval);                                                                         \
 }
 
             (m)->NextuDNSEvent = ((rr)->LastAPTime + (rr)->ThisAPInterval);                                                                         \
 }
 
+#ifndef UNICAST_DISABLED
+
 // ***************************************************************************
 #if COMPILER_LIKES_PRAGMA_MARK
 #pragma mark - General Utility Functions
 // ***************************************************************************
 #if COMPILER_LIKES_PRAGMA_MARK
 #pragma mark - General Utility Functions
@@ -106,7 +108,9 @@ mDNSlocal void SetRecordRetry(mDNS *const m, AuthRecord *rr, mDNSu32 random)
 #pragma mark - Name Server List Management
 #endif
 
 #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, mDNSu32 timeout, mDNSBool cellIntf, mDNSu16 resGroupID)
+mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSs32 serviceID, const mDNSAddr *addr,
+                                        const mDNSIPPort port, mDNSu32 scoped, mDNSu32 timeout, mDNSBool cellIntf, mDNSu16 resGroupID, mDNSBool reqA,
+                                        mDNSBool reqAAAA, mDNSBool reqDO)
 {
     DNSServer **p = &m->DNSServers;
     DNSServer *tmp = mDNSNULL;
 {
     DNSServer **p = &m->DNSServers;
     DNSServer *tmp = mDNSNULL;
@@ -117,39 +121,65 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons
         return mDNSNULL;
     }
 
         return mDNSNULL;
     }
 
-    if (!d) d = (const domainname *)"";
+    if (!d) 
+        d = (const domainname *)"";
 
 
-    LogInfo("mDNS_AddDNSServer: Adding %#a for %##s, InterfaceID %p, scoped %d, resGroupID %d", addr, d->c, interface, scoped, resGroupID);
+    LogInfo("mDNS_AddDNSServer(%d): Adding %#a for %##s, InterfaceID %p, serviceID %u, scoped %d, resGroupID %d req_A is %s req_AAAA is %s cell %s req_DO is %s",
+        NumUnicastDNSServers, addr, d->c, interface, serviceID, scoped, resGroupID, reqA ? "True" : "False", reqAAAA ? "True" : "False",
+        cellIntf ? "True" : "False", reqDO ? "True" : "False");
 
 
-    if (m->mDNS_busy != m->mDNS_reentrancy+1)
-        LogMsg("mDNS_AddDNSServer: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+    mDNS_CheckLock(m);
 
 
-    while (*p)  // Check if we already have this {interface,address,port,domain} tuple registered
+    while (*p)  // Check if we already have this {interface,address,port,domain} tuple registered + reqA/reqAAAA bits
     {
     {
-        if ((*p)->scoped == scoped && (*p)->interface == interface && (*p)->teststate != DNSServer_Disabled &&
-            mDNSSameAddress(&(*p)->addr, addr) && mDNSSameIPPort((*p)->port, port) && SameDomainName(&(*p)->domain, d))
+        if ((*p)->scoped == scoped && (*p)->interface == interface && (*p)->serviceID == serviceID && (*p)->teststate != DNSServer_Disabled &&
+            mDNSSameAddress(&(*p)->addr, addr) && mDNSSameIPPort((*p)->port, port) && SameDomainName(&(*p)->domain, d) && 
+            (*p)->req_A == reqA && (*p)->req_AAAA == reqAAAA)
         {
         {
-            if (!((*p)->flags & DNSServer_FlagDelete)) debugf("Note: DNS Server %#a:%d for domain %##s (%p) registered more than once", addr, mDNSVal16(port), d->c, interface);
-            (*p)->flags &= ~DNSServer_FlagDelete;
+            if (!((*p)->flags & DNSServer_FlagDelete)) 
+                debugf("Note: DNS Server %#a:%d for domain %##s (%p) registered more than once", addr, mDNSVal16(port), d->c, interface);
             tmp = *p;
             *p = tmp->next;
             tmp->next = mDNSNULL;
         }
         else
             tmp = *p;
             *p = tmp->next;
             tmp->next = mDNSNULL;
         }
         else
+        {
             p=&(*p)->next;
             p=&(*p)->next;
+        }
     }
 
     }
 
-    if (tmp) *p = tmp; // move to end of list, to ensure ordering from platform layer
+    // NumUnicastDNSServers is the count of active DNS servers i.e., ones that are not marked
+    // with DNSServer_FlagDelete. We should increment it:
+    //
+    // 1) When we add a new DNS server
+    // 2) When we resurrect a old DNS server that is marked with DNSServer_FlagDelete
+    //
+    // Don't increment when we resurrect a DNS server that is not marked with DNSServer_FlagDelete.
+    // We have already accounted for it when it was added for the first time. This case happens when
+    // we add DNS servers with the same address multiple times (mis-configuration).
+
+    if (!tmp || (tmp->flags & DNSServer_FlagDelete))
+        NumUnicastDNSServers++;
+
+
+    if (tmp)
+    {
+        tmp->flags &= ~DNSServer_FlagDelete;
+        *p = tmp; // move to end of list, to ensure ordering from platform layer
+    }
     else
     {
         // allocate, add to list
         *p = mDNSPlatformMemAllocate(sizeof(**p));
     else
     {
         // allocate, add to list
         *p = mDNSPlatformMemAllocate(sizeof(**p));
-        if (!*p) LogMsg("Error: mDNS_AddDNSServer - malloc");
+        if (!*p)
+        {
+            LogMsg("Error: mDNS_AddDNSServer - malloc");
+        }
         else
         {
         else
         {
-            NumUnicastDNSServers++;
             (*p)->scoped    = scoped;
             (*p)->interface = interface;
             (*p)->scoped    = scoped;
             (*p)->interface = interface;
+            (*p)->serviceID = serviceID;
             (*p)->addr      = *addr;
             (*p)->port      = port;
             (*p)->flags     = DNSServer_FlagNew;
             (*p)->addr      = *addr;
             (*p)->port      = port;
             (*p)->flags     = DNSServer_FlagNew;
@@ -157,6 +187,14 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons
             (*p)->lasttest  = m->timenow - INIT_UCAST_POLL_INTERVAL;
             (*p)->timeout   = timeout;
             (*p)->cellIntf  = cellIntf;
             (*p)->lasttest  = m->timenow - INIT_UCAST_POLL_INTERVAL;
             (*p)->timeout   = timeout;
             (*p)->cellIntf  = cellIntf;
+            (*p)->req_A     = reqA;
+            (*p)->req_AAAA  = reqAAAA;
+            (*p)->req_DO    = reqDO;
+            // We start off assuming that the DNS server is not DNSSEC aware and
+            // when we receive the first response to a DNSSEC question, we set
+            // it to true.
+            (*p)->DNSSECAware = mDNSfalse;
+            (*p)->retransDO = 0;
             AssignDomainName(&(*p)->domain, d);
             (*p)->next = mDNSNULL;
         }
             AssignDomainName(&(*p)->domain, d);
             (*p)->next = mDNSNULL;
         }
@@ -172,17 +210,21 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons
 // PenalizeDNSServer is called when the number of queries to the unicast
 // DNS server exceeds MAX_UCAST_UNANSWERED_QUERIES or when we receive an
 // error e.g., SERV_FAIL from DNS server.
 // PenalizeDNSServer is called when the number of queries to the unicast
 // DNS server exceeds MAX_UCAST_UNANSWERED_QUERIES or when we receive an
 // error e.g., SERV_FAIL from DNS server.
-mDNSexport void PenalizeDNSServer(mDNS *const m, DNSQuestion *q)
+mDNSexport void PenalizeDNSServer(mDNS *const m, DNSQuestion *q, mDNSOpaque16 responseFlags)
 {
     DNSServer *new;
     DNSServer *orig = q->qDNSServer;
 
 {
     DNSServer *new;
     DNSServer *orig = q->qDNSServer;
 
-    if (m->mDNS_busy != m->mDNS_reentrancy+1)
-        LogMsg("PenalizeDNSServer: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+    mDNS_CheckLock(m);
 
     LogInfo("PenalizeDNSServer: Penalizing DNS server %#a question for question %p %##s (%s) SuppressUnusable %d",
             (q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL), q, q->qname.c, DNSTypeName(q->qtype), q->SuppressUnusable);
 
 
     LogInfo("PenalizeDNSServer: Penalizing DNS server %#a question for question %p %##s (%s) SuppressUnusable %d",
             (q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL), q, q->qname.c, DNSTypeName(q->qtype), q->SuppressUnusable);
 
+    // If we get error from any DNS server, remember the error. If all of the servers,
+    // return the error, then return the first error. 
+    if (mDNSOpaque16IsZero(q->responseFlags))
+        q->responseFlags = responseFlags;
+
     // After we reset the qDNSServer to NULL, we could get more SERV_FAILS that might end up
     // peanlizing again.
     if (!q->qDNSServer) goto end;
     // After we reset the qDNSServer to NULL, we could get more SERV_FAILS that might end up
     // peanlizing again.
     if (!q->qDNSServer) goto end;
@@ -307,8 +349,7 @@ mDNSexport DomainAuthInfo *GetAuthInfoForName_internal(mDNS *m, const domainname
 {
     DomainAuthInfo **p = &m->AuthInfoList;
 
 {
     DomainAuthInfo **p = &m->AuthInfoList;
 
-    if (m->mDNS_busy != m->mDNS_reentrancy+1)
-        LogMsg("GetAuthInfoForName_internal: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+    mDNS_CheckLock(m);
 
     // First purge any dead keys from the list
     while (*p)
 
     // First purge any dead keys from the list
     while (*p)
@@ -419,60 +460,226 @@ mDNSexport mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info,
 #pragma mark - NAT Traversal
 #endif
 
 #pragma mark - NAT Traversal
 #endif
 
-mDNSlocal mStatus uDNS_SendNATMsg(mDNS *m, NATTraversalInfo *info)
+// Keep track of when to request/refresh the external address using NAT-PMP or UPnP/IGD,
+// and do so when necessary
+mDNSlocal mStatus uDNS_RequestAddress(mDNS *m)
 {
     mStatus err = mStatus_NoError;
 {
     mStatus err = mStatus_NoError;
+    
+    if (!m->NATTraversals)
+    {
+        m->retryGetAddr = NonZeroTime(m->timenow + 0x78000000);
+        LogInfo("uDNS_RequestAddress: Setting retryGetAddr to future");
+    }
+    else if (m->timenow - m->retryGetAddr >= 0)
+    {
+        if (mDNSv4AddrIsRFC1918(&m->Router.ip.v4))
+        {
+            static NATAddrRequest req = {NATMAP_VERS, NATOp_AddrRequest};
+            static mDNSu8* start = (mDNSu8*)&req;
+            mDNSu8* end = start + sizeof(NATAddrRequest);
+            err = mDNSPlatformSendUDP(m, start, end, 0, mDNSNULL, &m->Router, NATPMPPort, mDNSfalse);
+            debugf("uDNS_RequestAddress: Sent NAT-PMP external address request %d", err);
+            
+#ifdef _LEGACY_NAT_TRAVERSAL_
+            if (mDNSIPPortIsZero(m->UPnPRouterPort) || mDNSIPPortIsZero(m->UPnPSOAPPort))
+            {
+                LNT_SendDiscoveryMsg(m);
+                debugf("uDNS_RequestAddress: LNT_SendDiscoveryMsg");
+            }
+            else
+            {
+                mStatus lnterr = LNT_GetExternalAddress(m);
+                if (lnterr)
+                    LogMsg("uDNS_RequestAddress: LNT_GetExternalAddress returned error %d", lnterr);
+                
+                err = err ? err : lnterr; // NAT-PMP error takes precedence
+            }
+#endif // _LEGACY_NAT_TRAVERSAL_
+        }
 
 
-    // send msg if we have a router and it is a private address
-    if (!mDNSIPv4AddressIsZero(m->Router.ip.v4) && mDNSv4AddrIsRFC1918(&m->Router.ip.v4))
+        // Always update the interval and retry time, so that even if we fail to send the
+        // packet, we won't spin in an infinite loop repeatedly failing to send the packet
+        if (m->retryIntervalGetAddr < NATMAP_INIT_RETRY)
+        {
+            m->retryIntervalGetAddr = NATMAP_INIT_RETRY;
+        }
+        else if (m->retryIntervalGetAddr < NATMAP_MAX_RETRY_INTERVAL / 2)
+        {
+            m->retryIntervalGetAddr *= 2;
+        }
+        else
+        {
+            m->retryIntervalGetAddr = NATMAP_MAX_RETRY_INTERVAL;
+        }
+
+        m->retryGetAddr = NonZeroTime(m->timenow + m->retryIntervalGetAddr);
+    }
+    else
     {
     {
-        union { NATAddrRequest NATAddrReq; NATPortMapRequest NATPortReq; } u = { { NATMAP_VERS, NATOp_AddrRequest } } ;
-        const mDNSu8 *end = (mDNSu8 *)&u + sizeof(NATAddrRequest);
+        debugf("uDNS_RequestAddress: Not time to send address request");
+    }
+
+    // Always update NextScheduledNATOp, even if we didn't change retryGetAddr, so we'll
+    // be called when we need to send the request(s)
+    if (m->NextScheduledNATOp - m->retryGetAddr > 0)
+        m->NextScheduledNATOp = m->retryGetAddr;
+
+    return err;
+}
 
 
-        if (info)           // For NATOp_MapUDP and NATOp_MapTCP, fill in additional fields
+mDNSlocal mStatus uDNS_SendNATMsg(mDNS *m, NATTraversalInfo *info, mDNSBool usePCP)
+{
+    mStatus err = mStatus_NoError;
+
+    if (!info)
+    {
+        LogMsg("uDNS_SendNATMsg called unexpectedly with NULL info");
+        return mStatus_BadParamErr;
+    }
+    
+    // send msg if the router's address is private (which means it's non-zero)
+    if (mDNSv4AddrIsRFC1918(&m->Router.ip.v4))
+    {
+        if (!usePCP)
+        {
+            if (!info->sentNATPMP)
+            {
+                if (info->Protocol)
+                {
+                    static NATPortMapRequest NATPortReq;
+                    static const mDNSu8* end = (mDNSu8 *)&NATPortReq + sizeof(NATPortMapRequest);
+                    mDNSu8 *p = (mDNSu8 *)&NATPortReq.NATReq_lease;
+                    
+                    NATPortReq.vers    = NATMAP_VERS;
+                    NATPortReq.opcode  = info->Protocol;
+                    NATPortReq.unused  = zeroID;
+                    NATPortReq.intport = info->IntPort;
+                    NATPortReq.extport = info->RequestedPort;
+                    p[0] = (mDNSu8)((info->NATLease >> 24) &  0xFF);
+                    p[1] = (mDNSu8)((info->NATLease >> 16) &  0xFF);
+                    p[2] = (mDNSu8)((info->NATLease >>  8) &  0xFF);
+                    p[3] = (mDNSu8)( info->NATLease        &  0xFF);
+            
+                    err = mDNSPlatformSendUDP(m, (mDNSu8 *)&NATPortReq, end, 0, mDNSNULL, &m->Router, NATPMPPort, mDNSfalse);
+                    debugf("uDNS_SendNATMsg: Sent NAT-PMP mapping request %d", err);
+                }
+
+                // In case the address request already went out for another NAT-T,
+                // set the NewAddress to the currently known global external address, so
+                // Address-only operations will get the callback immediately
+                info->NewAddress = m->ExtAddress;
+
+                // Remember that we just sent a NAT-PMP packet, so we won't resend one later.
+                // We do this because the NAT-PMP "Unsupported Version" response has no
+                // information about the (PCP) request that triggered it, so we must send
+                // NAT-PMP requests for all operations. Without this, we'll send n PCP
+                // requests for n operations, receive n NAT-PMP "Unsupported Version"
+                // responses, and send n NAT-PMP requests for each of those responses,
+                // resulting in (n + n^2) packets sent. We only want to send 2n packets:
+                // n PCP requests followed by n NAT-PMP requests.
+                info->sentNATPMP = mDNStrue;
+            }
+        }
+        else
         {
         {
-            mDNSu8 *p = (mDNSu8 *)&u.NATPortReq.NATReq_lease;
-            u.NATPortReq.opcode  = info->Protocol;
-            u.NATPortReq.unused  = zeroID;
-            u.NATPortReq.intport = info->IntPort;
-            u.NATPortReq.extport = info->RequestedPort;
+            PCPMapRequest req;
+            mDNSu8* start = (mDNSu8*)&req;
+            mDNSu8* end = start + sizeof(req);
+            mDNSu8* p = (mDNSu8*)&req.lifetime;
+            
+            req.version = PCP_VERS;
+            req.opCode = PCPOp_Map;
+            req.reserved = zeroID;
+            
             p[0] = (mDNSu8)((info->NATLease >> 24) &  0xFF);
             p[1] = (mDNSu8)((info->NATLease >> 16) &  0xFF);
             p[2] = (mDNSu8)((info->NATLease >>  8) &  0xFF);
             p[3] = (mDNSu8)( info->NATLease        &  0xFF);
             p[0] = (mDNSu8)((info->NATLease >> 24) &  0xFF);
             p[1] = (mDNSu8)((info->NATLease >> 16) &  0xFF);
             p[2] = (mDNSu8)((info->NATLease >>  8) &  0xFF);
             p[3] = (mDNSu8)( info->NATLease        &  0xFF);
-            end = (mDNSu8 *)&u + sizeof(NATPortMapRequest);
-        }
-
-        err = mDNSPlatformSendUDP(m, (mDNSu8 *)&u, end, 0, mDNSNULL, &m->Router, NATPMPPort, mDNSfalse);
+            
+            mDNSAddrMapIPv4toIPv6(&m->AdvertisedV4.ip.v4, &req.clientAddr);
+            
+            req.nonce[0] = m->PCPNonce[0];
+            req.nonce[1] = m->PCPNonce[1];
+            req.nonce[2] = m->PCPNonce[2];
+            
+            req.protocol = (info->Protocol == NATOp_MapUDP ? PCPProto_UDP : PCPProto_TCP);
+            
+            req.reservedMapOp[0] = 0;
+            req.reservedMapOp[1] = 0;
+            req.reservedMapOp[2] = 0;
+            
+            req.intPort = info->Protocol ? info->IntPort : DiscardPort;
+            req.extPort = info->RequestedPort;
+            
+            // Since we only support IPv4, even if using the all-zeros address, map it, so
+            // the PCP gateway will give us an IPv4 address & not an IPv6 address.
+            mDNSAddrMapIPv4toIPv6(&info->NewAddress, &req.extAddress);
+
+            err = mDNSPlatformSendUDP(m, start, end, 0, mDNSNULL, &m->Router, NATPMPPort, mDNSfalse);
+            debugf("uDNS_SendNATMsg: Sent PCP Mapping request %d", err);
+
+            // Unset the sentNATPMP flag, so that we'll send a NAT-PMP packet if we
+            // receive a NAT-PMP "Unsupported Version" packet. This will result in every
+            // renewal, retransmission, etc. being tried first as PCP, then if a NAT-PMP
+            // "Unsupported Version" response is received, fall-back & send the request
+            // using NAT-PMP.
+            info->sentNATPMP = mDNSfalse;
 
 #ifdef _LEGACY_NAT_TRAVERSAL_
 
 #ifdef _LEGACY_NAT_TRAVERSAL_
-        if (mDNSIPPortIsZero(m->UPnPRouterPort) || mDNSIPPortIsZero(m->UPnPSOAPPort)) LNT_SendDiscoveryMsg(m);
-        else if (info) err = LNT_MapPort(m, info);
-        else err = LNT_GetExternalAddress(m);
+            if (mDNSIPPortIsZero(m->UPnPRouterPort) || mDNSIPPortIsZero(m->UPnPSOAPPort))
+            {
+                LNT_SendDiscoveryMsg(m);
+                debugf("uDNS_SendNATMsg: LNT_SendDiscoveryMsg");
+            }
+            else
+            {
+                mStatus lnterr = LNT_MapPort(m, info);
+                if (lnterr)
+                    LogMsg("uDNS_SendNATMsg: LNT_MapPort returned error %d", lnterr);
+                
+                err = err ? err : lnterr; // PCP error takes precedence
+            }
 #endif // _LEGACY_NAT_TRAVERSAL_
 #endif // _LEGACY_NAT_TRAVERSAL_
+        }
     }
     }
+
     return(err);
 }
 
     return(err);
 }
 
-mDNSexport void RecreateNATMappings(mDNS *const m)
+mDNSexport void RecreateNATMappings(mDNS *const m, const mDNSu32 waitTicks)
 {
 {
+    mDNSu32 when = NonZeroTime(m->timenow + waitTicks);
     NATTraversalInfo *n;
     for (n = m->NATTraversals; n; n=n->next)
     {
         n->ExpiryTime    = 0;       // Mark this mapping as expired
         n->retryInterval = NATMAP_INIT_RETRY;
     NATTraversalInfo *n;
     for (n = m->NATTraversals; n; n=n->next)
     {
         n->ExpiryTime    = 0;       // Mark this mapping as expired
         n->retryInterval = NATMAP_INIT_RETRY;
-        n->retryPortMap  = m->timenow;
+        n->retryPortMap  = when;
+        n->lastSuccessfulProtocol = NATTProtocolNone;
+        if (!n->Protocol) n->NewResult = mStatus_NoError;
 #ifdef _LEGACY_NAT_TRAVERSAL_
         if (n->tcpInfo.sock) { mDNSPlatformTCPCloseConnection(n->tcpInfo.sock); n->tcpInfo.sock = mDNSNULL; }
 #endif // _LEGACY_NAT_TRAVERSAL_
     }
 
 #ifdef _LEGACY_NAT_TRAVERSAL_
         if (n->tcpInfo.sock) { mDNSPlatformTCPCloseConnection(n->tcpInfo.sock); n->tcpInfo.sock = mDNSNULL; }
 #endif // _LEGACY_NAT_TRAVERSAL_
     }
 
+    m->PCPNonce[0] = mDNSRandom(-1);
+    m->PCPNonce[1] = mDNSRandom(-1);
+    m->PCPNonce[2] = mDNSRandom(-1);
+    m->retryIntervalGetAddr = 0;
+    m->retryGetAddr = when;
+
+#ifdef _LEGACY_NAT_TRAVERSAL_
+    LNT_ClearState(m);
+#endif // _LEGACY_NAT_TRAVERSAL_
+
     m->NextScheduledNATOp = m->timenow;     // Need to send packets immediately
 }
 
 mDNSexport void natTraversalHandleAddressReply(mDNS *const m, mDNSu16 err, mDNSv4Addr ExtAddr)
 {
     static mDNSu16 last_err = 0;
     m->NextScheduledNATOp = m->timenow;     // Need to send packets immediately
 }
 
 mDNSexport void natTraversalHandleAddressReply(mDNS *const m, mDNSu16 err, mDNSv4Addr ExtAddr)
 {
     static mDNSu16 last_err = 0;
+    NATTraversalInfo *n;
 
     if (err)
     {
 
     if (err)
     {
@@ -488,11 +695,9 @@ mDNSexport void natTraversalHandleAddressReply(mDNS *const m, mDNSu16 err, mDNSv
             err = NATErr_NetFail; // fake error to handle routers that pathologically report success with the zero address
     }
 
             err = NATErr_NetFail; // fake error to handle routers that pathologically report success with the zero address
     }
 
-    if (!mDNSSameIPv4Address(m->ExternalAddress, ExtAddr))
-    {
-        m->ExternalAddress = ExtAddr;
-        RecreateNATMappings(m);     // Also sets NextScheduledNATOp for us
-    }
+    // Globally remember the most recently discovered address, so it can be used in each
+    // new NATTraversal structure
+    m->ExtAddress = ExtAddr;
 
     if (!err) // Success, back-off to maximum interval
         m->retryIntervalGetAddr = NATMAP_MAX_RETRY_INTERVAL;
 
     if (!err) // Success, back-off to maximum interval
         m->retryIntervalGetAddr = NATMAP_MAX_RETRY_INTERVAL;
@@ -501,10 +706,40 @@ mDNSexport void natTraversalHandleAddressReply(mDNS *const m, mDNSu16 err, mDNSv
     // else back-off normally in case of pathological failures
 
     m->retryGetAddr = m->timenow + m->retryIntervalGetAddr;
     // else back-off normally in case of pathological failures
 
     m->retryGetAddr = m->timenow + m->retryIntervalGetAddr;
-    if (m->NextScheduledNATOp - m->retryIntervalGetAddr > 0)
-        m->NextScheduledNATOp = m->retryIntervalGetAddr;
+    if (m->NextScheduledNATOp - m->retryGetAddr > 0)
+        m->NextScheduledNATOp = m->retryGetAddr;
 
     last_err = err;
 
     last_err = err;
+    
+    for (n = m->NATTraversals; n; n=n->next)
+    {
+        // We should change n->NewAddress only when n is one of:
+        // 1) a mapping operation that most recently succeeded using NAT-PMP or UPnP/IGD,
+        //    because such an operation needs the update now. If the lastSuccessfulProtocol
+        //    is currently none, then natTraversalHandlePortMapReplyWithAddress() will be
+        //    called should NAT-PMP or UPnP/IGD succeed in the future.
+        // 2) an address-only operation that did not succeed via PCP, because when such an
+        //    operation succeeds via PCP, it's for the TCP discard port just to learn the
+        //    address. And that address may be different than the external address
+        //    discovered via NAT-PMP or UPnP/IGD. If the lastSuccessfulProtocol
+        //    is currently none, we must update the NewAddress as PCP may not succeed.
+        if (!mDNSSameIPv4Address(n->NewAddress, ExtAddr) &&
+             (n->Protocol ?
+               (n->lastSuccessfulProtocol == NATTProtocolNATPMP || n->lastSuccessfulProtocol == NATTProtocolUPNPIGD) :
+               (n->lastSuccessfulProtocol != NATTProtocolPCP)))
+        {
+            // Needs an update immediately
+            n->NewAddress    = ExtAddr;
+            n->ExpiryTime    = 0;
+            n->retryInterval = NATMAP_INIT_RETRY;
+            n->retryPortMap  = m->timenow;
+#ifdef _LEGACY_NAT_TRAVERSAL_
+            if (n->tcpInfo.sock) { mDNSPlatformTCPCloseConnection(n->tcpInfo.sock); n->tcpInfo.sock = mDNSNULL; }
+#endif // _LEGACY_NAT_TRAVERSAL_
+
+            m->NextScheduledNATOp = m->timenow;     // Need to send packets immediately
+        }
+    }
 }
 
 // Both places that call NATSetNextRenewalTime() update m->NextScheduledNATOp correctly afterwards
 }
 
 // Both places that call NATSetNextRenewalTime() update m->NextScheduledNATOp correctly afterwards
@@ -516,16 +751,15 @@ mDNSlocal void NATSetNextRenewalTime(mDNS *const m, NATTraversalInfo *n)
     n->retryPortMap = m->timenow + n->retryInterval;
 }
 
     n->retryPortMap = m->timenow + n->retryInterval;
 }
 
-// Note: When called from handleLNTPortMappingResponse() only pkt->err, pkt->extport and pkt->NATRep_lease fields are filled in
-mDNSexport void natTraversalHandlePortMapReply(mDNS *const m, NATTraversalInfo *n, const mDNSInterfaceID InterfaceID, mDNSu16 err, mDNSIPPort extport, mDNSu32 lease)
+mDNSlocal void natTraversalHandlePortMapReplyWithAddress(mDNS *const m, NATTraversalInfo *n, const mDNSInterfaceID InterfaceID, mDNSu16 err, mDNSv4Addr extaddr, mDNSIPPort extport, mDNSu32 lease, NATTProtocol protocol)
 {
 {
-    const char *prot = n->Protocol == NATOp_MapUDP ? "UDP" : n->Protocol == NATOp_MapTCP ? "TCP" : "?";
+    const char *prot = n->Protocol == 0 ? "Add" : n->Protocol == NATOp_MapUDP ? "UDP" : n->Protocol == NATOp_MapTCP ? "TCP" : "???";
     (void)prot;
     n->NewResult = err;
     if (err || lease == 0 || mDNSIPPortIsZero(extport))
     {
     (void)prot;
     n->NewResult = err;
     if (err || lease == 0 || mDNSIPPortIsZero(extport))
     {
-        LogInfo("natTraversalHandlePortMapReply: %p Response %s Port %5d External Port %5d lease %d error %d",
-                n, prot, mDNSVal16(n->IntPort), mDNSVal16(extport), lease, err);
+        LogInfo("natTraversalHandlePortMapReplyWithAddress: %p Response %s Port %5d External %.4a:%d lease %d error %d",
+                n, prot, mDNSVal16(n->IntPort), &extaddr, mDNSVal16(extport), lease, err);
         n->retryInterval = NATMAP_MAX_RETRY_INTERVAL;
         n->retryPortMap = m->timenow + NATMAP_MAX_RETRY_INTERVAL;
         // No need to set m->NextScheduledNATOp here, since we're only ever extending the m->retryPortMap time
         n->retryInterval = NATMAP_MAX_RETRY_INTERVAL;
         n->retryPortMap = m->timenow + NATMAP_MAX_RETRY_INTERVAL;
         // No need to set m->NextScheduledNATOp here, since we're only ever extending the m->retryPortMap time
@@ -538,21 +772,33 @@ mDNSexport void natTraversalHandlePortMapReply(mDNS *const m, NATTraversalInfo *
             lease = 999999999UL / mDNSPlatformOneSecond;
         n->ExpiryTime = NonZeroTime(m->timenow + lease * mDNSPlatformOneSecond);
 
             lease = 999999999UL / mDNSPlatformOneSecond;
         n->ExpiryTime = NonZeroTime(m->timenow + lease * mDNSPlatformOneSecond);
 
-        if (!mDNSSameIPPort(n->RequestedPort, extport))
-            LogInfo("natTraversalHandlePortMapReply: %p Response %s Port %5d External Port %5d changed to %5d",
-                    n, prot, mDNSVal16(n->IntPort), mDNSVal16(n->RequestedPort), mDNSVal16(extport));
+        if (!mDNSSameIPv4Address(n->NewAddress, extaddr) || !mDNSSameIPPort(n->RequestedPort, extport))
+            LogInfo("natTraversalHandlePortMapReplyWithAddress: %p %s Response %s Port %5d External %.4a:%d changed to %.4a:%d lease %d",
+                    n,
+                    (n->lastSuccessfulProtocol == NATTProtocolNone    ? "None    " :
+                     n->lastSuccessfulProtocol == NATTProtocolNATPMP  ? "NAT-PMP " :
+                     n->lastSuccessfulProtocol == NATTProtocolUPNPIGD ? "UPnP/IGD" :
+                     n->lastSuccessfulProtocol == NATTProtocolPCP     ? "PCP     " :
+                     /* else */                                         "Unknown " ),
+                    prot, mDNSVal16(n->IntPort), &n->NewAddress, mDNSVal16(n->RequestedPort),
+                    &extaddr, mDNSVal16(extport), lease);
 
         n->InterfaceID   = InterfaceID;
 
         n->InterfaceID   = InterfaceID;
-        n->RequestedPort = extport;
-
-        LogInfo("natTraversalHandlePortMapReply: %p Response %s Port %5d External Port %5d lease %d",
-                n, prot, mDNSVal16(n->IntPort), mDNSVal16(extport), lease);
+        n->NewAddress    = extaddr;
+        if (n->Protocol) n->RequestedPort = extport; // Don't report the (PCP) external port to address-only operations
+        n->lastSuccessfulProtocol = protocol;
 
         NATSetNextRenewalTime(m, n);            // Got our port mapping; now set timer to renew it at halfway point
         m->NextScheduledNATOp = m->timenow;     // May need to invoke client callback immediately
     }
 }
 
 
         NATSetNextRenewalTime(m, n);            // Got our port mapping; now set timer to renew it at halfway point
         m->NextScheduledNATOp = m->timenow;     // May need to invoke client callback immediately
     }
 }
 
+// To be called for NAT-PMP or UPnP/IGD mappings, to use currently discovered (global) address
+mDNSexport void natTraversalHandlePortMapReply(mDNS *const m, NATTraversalInfo *n, const mDNSInterfaceID InterfaceID, mDNSu16 err, mDNSIPPort extport, mDNSu32 lease, NATTProtocol protocol)
+{
+    natTraversalHandlePortMapReplyWithAddress(m, n, InterfaceID, err, m->ExtAddress, extport, lease, protocol);
+}
+
 // Must be called with the mDNS_Lock held
 mDNSexport mStatus mDNS_StartNATOperation_internal(mDNS *const m, NATTraversalInfo *traversal)
 {
 // Must be called with the mDNS_Lock held
 mDNSexport mStatus mDNS_StartNATOperation_internal(mDNS *const m, NATTraversalInfo *traversal)
 {
@@ -587,7 +833,10 @@ mDNSexport mStatus mDNS_StartNATOperation_internal(mDNS *const m, NATTraversalIn
     traversal->retryInterval   = NATMAP_INIT_RETRY;
     traversal->retryPortMap    = m->timenow;
     traversal->NewResult       = mStatus_NoError;
     traversal->retryInterval   = NATMAP_INIT_RETRY;
     traversal->retryPortMap    = m->timenow;
     traversal->NewResult       = mStatus_NoError;
+    traversal->lastSuccessfulProtocol = NATTProtocolNone;
+    traversal->sentNATPMP      = mDNSfalse;
     traversal->ExternalAddress = onesIPv4Addr;
     traversal->ExternalAddress = onesIPv4Addr;
+    traversal->NewAddress      = zerov4Addr;
     traversal->ExternalPort    = zeroIPPort;
     traversal->Lifetime        = 0;
     traversal->Result          = mStatus_NoError;
     traversal->ExternalPort    = zeroIPPort;
     traversal->Lifetime        = 0;
     traversal->Result          = mStatus_NoError;
@@ -605,6 +854,14 @@ mDNSexport mStatus mDNS_StartNATOperation_internal(mDNS *const m, NATTraversalIn
         m->retryIntervalGetAddr = NATMAP_INIT_RETRY;
     }
 
         m->retryIntervalGetAddr = NATMAP_INIT_RETRY;
     }
 
+    // If this is an address-only operation, initialize to the current global address,
+    // or (in non-PCP environments) we won't know the address until the next external
+    // address request/response.
+    if (!traversal->Protocol)
+    {
+        traversal->NewAddress = m->ExtAddress;
+    }
+
     m->NextScheduledNATOp = m->timenow; // This will always trigger sending the packet ASAP, and generate client callback if necessary
 
     *n = traversal;     // Append new NATTraversalInfo to the end of our list
     m->NextScheduledNATOp = m->timenow; // This will always trigger sending the packet ASAP, and generate client callback if necessary
 
     *n = traversal;     // Append new NATTraversalInfo to the end of our list
@@ -633,23 +890,44 @@ mDNSexport mStatus mDNS_StopNATOperation_internal(mDNS *m, NATTraversalInfo *tra
     if (m->CurrentNATTraversal == traversal)
         m->CurrentNATTraversal = m->CurrentNATTraversal->next;
 
     if (m->CurrentNATTraversal == traversal)
         m->CurrentNATTraversal = m->CurrentNATTraversal->next;
 
-    if (traversal->Protocol)
-        for (p = m->NATTraversals; p; p=p->next)
-            if (traversal->Protocol == p->Protocol && mDNSSameIPPort(traversal->IntPort, p->IntPort))
-            {
-                if (!mDNSSameIPPort(traversal->IntPort, SSHPort))
-                    LogMsg("Warning: Removed port mapping request %p Prot %d Int %d TTL %d "
-                           "duplicates existing port mapping request %p Prot %d Int %d TTL %d",
-                           traversal, traversal->Protocol, mDNSVal16(traversal->IntPort), traversal->NATLease,
-                           p,         p->Protocol, mDNSVal16(p->IntPort), p->NATLease);
-                unmap = mDNSfalse;
-            }
+    // If there is a match for the operation being stopped, don't send a deletion request (unmap)
+    for (p = m->NATTraversals; p; p=p->next)
+    {
+        if (traversal->Protocol ?
+            ((traversal->Protocol == p->Protocol && mDNSSameIPPort(traversal->IntPort, p->IntPort)) ||
+             (!p->Protocol && traversal->Protocol == NATOp_MapTCP && mDNSSameIPPort(traversal->IntPort, DiscardPort))) :
+            (!p->Protocol || (p->Protocol == NATOp_MapTCP && mDNSSameIPPort(p->IntPort, DiscardPort))))
+        {
+            LogInfo("Warning: Removed port mapping request %p Prot %d Int %d TTL %d "
+                    "duplicates existing port mapping request %p Prot %d Int %d TTL %d",
+                    traversal, traversal->Protocol, mDNSVal16(traversal->IntPort), traversal->NATLease,
+                            p,         p->Protocol, mDNSVal16(        p->IntPort),         p->NATLease);
+            unmap = mDNSfalse;
+        }
+    }
 
     if (traversal->ExpiryTime && unmap)
     {
         traversal->NATLease = 0;
         traversal->retryInterval = 0;
 
     if (traversal->ExpiryTime && unmap)
     {
         traversal->NATLease = 0;
         traversal->retryInterval = 0;
-        uDNS_SendNATMsg(m, traversal);
+        
+        // In case we most recently sent NAT-PMP, we need to set sentNATPMP to false so
+        // that we'll send a NAT-PMP request to destroy the mapping. We do this because
+        // the NATTraversal struct has already been cut from the list, and the client
+        // layer will destroy the memory upon returning from this function, so we can't
+        // try PCP first and then fall-back to NAT-PMP. That is, if we most recently
+        // created/renewed the mapping using NAT-PMP, we need to destroy it using NAT-PMP
+        // now, because we won't get a chance later.
+        traversal->sentNATPMP = mDNSfalse;
+
+        // Both NAT-PMP & PCP RFCs state that the suggested port in deletion requests
+        // should be zero. And for PCP, the suggested external address should also be
+        // zero, specifically, the all-zeros IPv4-mapped address, since we would only
+        // would have requested an IPv4 address.
+        traversal->RequestedPort = zeroIPPort;
+        traversal->NewAddress = zerov4Addr;
+        
+        uDNS_SendNATMsg(m, traversal, traversal->lastSuccessfulProtocol != NATTProtocolNATPMP);
     }
 
     // Even if we DIDN'T make a successful UPnP mapping yet, we might still have a partially-open TCP connection we need to clean up
     }
 
     // Even if we DIDN'T make a successful UPnP mapping yet, we might still have a partially-open TCP connection we need to clean up
@@ -880,15 +1158,15 @@ mDNSexport uDNS_LLQType uDNS_recvLLQResponse(mDNS *const m, const DNSMessage *co
 
                     // Don't reset the state to IntialRequest as we may write that to the dynamic store
                     // and PrefPane might wrongly think that we are "Starting" instead of "Polling". If
 
                     // Don't reset the state to IntialRequest as we may write that to the dynamic store
                     // and PrefPane might wrongly think that we are "Starting" instead of "Polling". If
-                    // we are in polling state because of NAT-PMP disabled or DoubleNAT, next LLQNATCallback
+                    // we are in polling state because of PCP/NAT-PMP disabled or DoubleNAT, next LLQNATCallback
                     // would kick us back to LLQInitialRequest. So, resetting the state here may not be useful.
                     //
                     // would kick us back to LLQInitialRequest. So, resetting the state here may not be useful.
                     //
-                    // If we have a good NAT (neither NAT-PMP disabled nor Double-NAT), then we should not be
+                    // If we have a good NAT (neither PCP/NAT-PMP disabled nor Double-NAT), then we should not be
                     // possibly in polling state. To be safe, we want to retry from the start in that case
                     // as there may not be another LLQNATCallback
                     //
                     // NOTE: We can be in polling state if we cannot resolve the SOA record i.e, servAddr is set to
                     // possibly in polling state. To be safe, we want to retry from the start in that case
                     // as there may not be another LLQNATCallback
                     //
                     // NOTE: We can be in polling state if we cannot resolve the SOA record i.e, servAddr is set to
-                    // all ones. In that case, we would set it in LLQ_InitialRequest as it overrides the NAT-PMP or
+                    // all ones. In that case, we would set it in LLQ_InitialRequest as it overrides the PCP/NAT-PMP or
                     // Double-NAT state.
                     if (!mDNSAddressIsOnes(&q->servAddr) && !mDNSIPPortIsZero(m->LLQNAT.ExternalPort) &&
                         !m->LLQNAT.Result)
                     // Double-NAT state.
                     if (!mDNSAddressIsOnes(&q->servAddr) && !mDNSIPPortIsZero(m->LLQNAT.ExternalPort) &&
                         !m->LLQNAT.Result)
@@ -1024,7 +1302,12 @@ mDNSlocal void tcpCallback(TCPSocket *sock, void *context, mDNSBool ConnectionEs
             InitializeDNSMessage(&tcpInfo->request.h, q->TargetQID, (DNSSECQuestion(q) ? DNSSecQFlags : uQueryFlags));
             end = putQuestion(&tcpInfo->request, tcpInfo->request.data, tcpInfo->request.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass);
             if (DNSSECQuestion(q) && q->qDNSServer && !q->qDNSServer->cellIntf)
             InitializeDNSMessage(&tcpInfo->request.h, q->TargetQID, (DNSSECQuestion(q) ? DNSSecQFlags : uQueryFlags));
             end = putQuestion(&tcpInfo->request, tcpInfo->request.data, tcpInfo->request.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass);
             if (DNSSECQuestion(q) && q->qDNSServer && !q->qDNSServer->cellIntf)
-                end = putDNSSECOption(&tcpInfo->request, end, tcpInfo->request.data + AbsoluteMaxDNSMessageData);
+            {
+                if (q->ProxyQuestion)
+                    end = DNSProxySetAttributes(q, &tcpInfo->request.h, &tcpInfo->request, end, tcpInfo->request.data + AbsoluteMaxDNSMessageData);
+                else
+                    end = putDNSSECOption(&tcpInfo->request, end, tcpInfo->request.data + AbsoluteMaxDNSMessageData);
+            }
 
             AuthInfo = q->AuthInfo;     // Need to add TSIG to this message
         }
 
             AuthInfo = q->AuthInfo;     // Need to add TSIG to this message
         }
@@ -1241,7 +1524,7 @@ mDNSlocal tcpInfo_t *MakeTCPConn(mDNS *const m, const DNSMessage *const msg, con
     tcpInfo_t *info;
     mDNSBool useBackgroundTrafficClass;
 
     tcpInfo_t *info;
     mDNSBool useBackgroundTrafficClass;
 
-    useBackgroundTrafficClass = question ? question->UseBrackgroundTrafficClass : mDNSfalse;
+    useBackgroundTrafficClass = question ? question->UseBackgroundTrafficClass : mDNSfalse;
 
     if ((flags & kTCPSocketFlags_UseTLS) && (!hostname || !hostname->c[0]))
     { LogMsg("MakeTCPConn: TLS connection being setup with NULL hostname"); return mDNSNULL; }
 
     if ((flags & kTCPSocketFlags_UseTLS) && (!hostname || !hostname->c[0]))
     { LogMsg("MakeTCPConn: TLS connection being setup with NULL hostname"); return mDNSNULL; }
@@ -1294,7 +1577,7 @@ mDNSexport void DisposeTCPConn(struct tcpInfo_t *tcp)
 // Lock must be held
 mDNSexport void startLLQHandshake(mDNS *m, DNSQuestion *q)
 {
 // Lock must be held
 mDNSexport void startLLQHandshake(mDNS *m, DNSQuestion *q)
 {
-    if (mDNSIPv4AddressIsOnes(m->LLQNAT.ExternalAddress))
+    if (m->LLQNAT.clientContext != mDNSNULL) // LLQNAT just started, give it some time
     {
         LogInfo("startLLQHandshake: waiting for NAT status for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
         q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10);    // Retry in approx 15 minutes
     {
         LogInfo("startLLQHandshake: waiting for NAT status for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
         q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10);    // Retry in approx 15 minutes
@@ -1303,8 +1586,8 @@ mDNSexport void startLLQHandshake(mDNS *m, DNSQuestion *q)
         return;
     }
 
         return;
     }
 
-    // Either we don't have NAT-PMP support (ExternalPort is zero) or behind a Double NAT that may or
-    // may not have NAT-PMP support (NATResult is non-zero)
+    // Either we don't have {PCP, NAT-PMP, UPnP/IGD} support (ExternalPort is zero) or behind a Double NAT that may or
+    // may not have {PCP, NAT-PMP, UPnP/IGD} support (NATResult is non-zero)
     if (mDNSIPPortIsZero(m->LLQNAT.ExternalPort) || m->LLQNAT.Result)
     {
         LogInfo("startLLQHandshake: Cannot receive inbound packets; will poll for %##s (%s) External Port %d, NAT Result %d",
     if (mDNSIPPortIsZero(m->LLQNAT.ExternalPort) || m->LLQNAT.Result)
     {
         LogInfo("startLLQHandshake: Cannot receive inbound packets; will poll for %##s (%s) External Port %d, NAT Result %d",
@@ -1596,10 +1879,13 @@ mDNSlocal mStatus GetZoneData_StartQuery(mDNS *const m, ZoneData *zd, mDNSu16 qt
     zd->question.RetryWithSearchDomains = mDNSfalse;
     zd->question.TimeoutQuestion     = 0;
     zd->question.WakeOnResolve       = 0;
     zd->question.RetryWithSearchDomains = mDNSfalse;
     zd->question.TimeoutQuestion     = 0;
     zd->question.WakeOnResolve       = 0;
-    zd->question.UseBrackgroundTrafficClass = mDNSfalse;
+    zd->question.UseBackgroundTrafficClass = mDNSfalse;
     zd->question.ValidationRequired = 0;
     zd->question.ValidatingResponse = 0;
     zd->question.ValidationRequired = 0;
     zd->question.ValidatingResponse = 0;
+    zd->question.ProxyQuestion      = 0;
     zd->question.qnameOrig           = mDNSNULL;
     zd->question.qnameOrig           = mDNSNULL;
+    zd->question.AnonInfo            = mDNSNULL;
+    zd->question.pid                 = mDNSPlatformGetPID();
     zd->question.QuestionCallback    = GetZoneData_QuestionCallback;
     zd->question.QuestionContext     = zd;
 
     zd->question.QuestionCallback    = GetZoneData_QuestionCallback;
     zd->question.QuestionContext     = zd;
 
@@ -1966,8 +2252,7 @@ mDNSlocal void UpdateOneSRVRecord(mDNS *m, AuthRecord *rr)
            rr->resrec.name->c, newtarget,
            TargetChanged, HaveZoneData, mDNSVal16(port), NowNeedNATMAP, WereBehindNAT, PortWasMapped, NATChanged);
 
            rr->resrec.name->c, newtarget,
            TargetChanged, HaveZoneData, mDNSVal16(port), NowNeedNATMAP, WereBehindNAT, PortWasMapped, NATChanged);
 
-    if (m->mDNS_busy != m->mDNS_reentrancy+1)
-        LogMsg("UpdateOneSRVRecord: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+    mDNS_CheckLock(m);
 
     if (!TargetChanged && !NATChanged) return;
 
 
     if (!TargetChanged && !NATChanged) return;
 
@@ -2281,10 +2566,13 @@ mDNSlocal void GetStaticHostname(mDNS *m)
     q->RetryWithSearchDomains = mDNSfalse;
     q->TimeoutQuestion  = 0;
     q->WakeOnResolve    = 0;
     q->RetryWithSearchDomains = mDNSfalse;
     q->TimeoutQuestion  = 0;
     q->WakeOnResolve    = 0;
-    q->UseBrackgroundTrafficClass = mDNSfalse;
+    q->UseBackgroundTrafficClass = mDNSfalse;
     q->ValidationRequired = 0;
     q->ValidatingResponse = 0;
     q->ValidationRequired = 0;
     q->ValidatingResponse = 0;
+    q->ProxyQuestion      = 0;
     q->qnameOrig        = mDNSNULL;
     q->qnameOrig        = mDNSNULL;
+    q->AnonInfo         = mDNSNULL;
+    q->pid              = mDNSPlatformGetPID();
     q->QuestionCallback = FoundStaticHostname;
     q->QuestionContext  = mDNSNULL;
 
     q->QuestionCallback = FoundStaticHostname;
     q->QuestionContext  = mDNSNULL;
 
@@ -2338,7 +2626,7 @@ mDNSexport void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn)
         if (f6) mDNS_Deregister_internal(m, &hi->arv6, mDNS_Dereg_normal);
         // When both deregistrations complete we'll free the memory in the mStatus_MemFree callback
     }
         if (f6) mDNS_Deregister_internal(m, &hi->arv6, mDNS_Dereg_normal);
         // When both deregistrations complete we'll free the memory in the mStatus_MemFree callback
     }
-    if (!m->mDNS_busy) LogMsg("mDNS_RemoveDynDNSHostName: ERROR: Lock not held");
+    mDNS_CheckLock(m);
     m->NextSRVUpdate = NonZeroTime(m->timenow);
 }
 
     m->NextSRVUpdate = NonZeroTime(m->timenow);
 }
 
@@ -2405,18 +2693,20 @@ mDNSexport void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, co
             // If we have a non-zero IPv4 address, we should try immediately to see if we have a NAT gateway
             // If we have no IPv4 address, we don't want to be in quite such a hurry to report failures to our clients
             // <rdar://problem/6935929> Sleeping server sometimes briefly disappears over Back to My Mac after it wakes up
             // If we have a non-zero IPv4 address, we should try immediately to see if we have a NAT gateway
             // If we have no IPv4 address, we don't want to be in quite such a hurry to report failures to our clients
             // <rdar://problem/6935929> Sleeping server sometimes briefly disappears over Back to My Mac after it wakes up
-            m->ExternalAddress      = zerov4Addr;
-            m->retryIntervalGetAddr = NATMAP_INIT_RETRY;
-            m->retryGetAddr         = m->timenow + (v4addr ? 0 : mDNSPlatformOneSecond * 5);
-            m->NextScheduledNATOp   = m->timenow;
+            mDNSu32 waitSeconds = v4addr ? 0 : 5;
+            NATTraversalInfo *n;
+            m->ExtAddress           = zerov4Addr;
             m->LastNATMapResultCode = NATErr_None;
             m->LastNATMapResultCode = NATErr_None;
-#ifdef _LEGACY_NAT_TRAVERSAL_
-            LNT_ClearState(m);
-#endif // _LEGACY_NAT_TRAVERSAL_
-            LogInfo("mDNS_SetPrimaryInterfaceInfo:%s%s: retryGetAddr in %d %d",
+
+            RecreateNATMappings(m, mDNSPlatformOneSecond * waitSeconds);
+
+            for (n = m->NATTraversals; n; n=n->next)
+                n->NewAddress = zerov4Addr;
+
+            LogInfo("mDNS_SetPrimaryInterfaceInfo:%s%s: recreating NAT mappings in %d seconds",
                     v4Changed     ? " v4Changed"     : "",
                     RouterChanged ? " RouterChanged" : "",
                     v4Changed     ? " v4Changed"     : "",
                     RouterChanged ? " RouterChanged" : "",
-                    m->retryGetAddr - m->timenow, m->timenow);
+                    waitSeconds);
         }
 
         if (m->ReverseMap.ThisQInterval != -1) mDNS_StopQuery_internal(m, &m->ReverseMap);
         }
 
         if (m->ReverseMap.ThisQInterval != -1) mDNS_StopQuery_internal(m, &m->ReverseMap);
@@ -2649,8 +2939,7 @@ mDNSlocal void SendRecordRegistration(mDNS *const m, AuthRecord *rr)
     AuthInfo = GetAuthInfoForName_internal(m, rr->resrec.name);
     limit -= RRAdditionalSize(m, AuthInfo);
 
     AuthInfo = GetAuthInfoForName_internal(m, rr->resrec.name);
     limit -= RRAdditionalSize(m, AuthInfo);
 
-    if (m->mDNS_busy != m->mDNS_reentrancy+1)
-        LogMsg("SendRecordRegistration: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+    mDNS_CheckLock(m);
 
     if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4))
     {
 
     if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4))
     {
@@ -2878,7 +3167,7 @@ mDNSlocal AuthRecord *MarkRRForSending(mDNS *const m)
         else if (!AreRecordsMergeable(m, firstRR, rr, m->timenow + MERGE_DELAY_TIME)) continue;
 
         if (rr->SendRNow) LogMsg("MarkRRForSending: Resourcerecord %s already marked for sending", ARDisplayString(m, rr));
         else if (!AreRecordsMergeable(m, firstRR, rr, m->timenow + MERGE_DELAY_TIME)) continue;
 
         if (rr->SendRNow) LogMsg("MarkRRForSending: Resourcerecord %s already marked for sending", ARDisplayString(m, rr));
-        rr->SendRNow = mDNSInterfaceMark;
+        rr->SendRNow = uDNSInterfaceMark;
     }
 
     // We parsed through all records and found something to send. The services/records might
     }
 
     // We parsed through all records and found something to send. The services/records might
@@ -2898,10 +3187,10 @@ mDNSlocal AuthRecord *MarkRRForSending(mDNS *const m)
         for (rr = m->ResourceRecords; rr; rr = rr->next)
         {
             if ((rr->state != regState_Registered && rr->state != regState_Refresh) ||
         for (rr = m->ResourceRecords; rr; rr = rr->next)
         {
             if ((rr->state != regState_Registered && rr->state != regState_Refresh) ||
-                (rr->SendRNow == mDNSInterfaceMark) ||
+                (rr->SendRNow == uDNSInterfaceMark) ||
                 (!AreRecordsMergeable(m, firstRR, rr, m->timenow + rr->ThisAPInterval/2)))
                 continue;
                 (!AreRecordsMergeable(m, firstRR, rr, m->timenow + rr->ThisAPInterval/2)))
                 continue;
-            rr->SendRNow = mDNSInterfaceMark;
+            rr->SendRNow = uDNSInterfaceMark;
             acc++;
         }
         if (acc) LogInfo("MarkRRForSending: Accelereated %d records", acc);
             acc++;
         }
         if (acc) LogInfo("MarkRRForSending: Accelereated %d records", acc);
@@ -2947,7 +3236,7 @@ mDNSlocal mDNSBool SendGroupUpdates(mDNS *const m)
         zoneSize = 0;
         for (rr = startRR; rr; rr = rr->next)
         {
         zoneSize = 0;
         for (rr = startRR; rr; rr = rr->next)
         {
-            if (rr->SendRNow != mDNSInterfaceMark) continue;
+            if (rr->SendRNow != uDNSInterfaceMark) continue;
 
             rr->SendRNow = mDNSNULL;
 
 
             rr->SendRNow = mDNSNULL;
 
@@ -3006,7 +3295,7 @@ mDNSlocal mDNSBool SendGroupUpdates(mDNS *const m)
                 {
                     LogInfo("SendGroupUpdates: Skipping message %s, spaceleft %d, rrSize %d", ARDisplayString(m, rr), spaceleft, rrSize);
                     // Mark this as not sent so that the caller knows about it
                 {
                     LogInfo("SendGroupUpdates: Skipping message %s, spaceleft %d, rrSize %d", ARDisplayString(m, rr), spaceleft, rrSize);
                     // Mark this as not sent so that the caller knows about it
-                    rr->SendRNow = mDNSInterfaceMark;
+                    rr->SendRNow = uDNSInterfaceMark;
                     // We need to remove the merge delay so that we can send it immediately
                     rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL;
                     rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL;
                     // We need to remove the merge delay so that we can send it immediately
                     rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL;
                     rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL;
@@ -3075,7 +3364,7 @@ mDNSlocal void CheckGroupRecordUpdates(mDNS *const m)
                 // SendRecordRegistrtion might delete the rr from list, hence
                 // dereference nextRR before calling the function
                 nextRR = rr->next;
                 // SendRecordRegistrtion might delete the rr from list, hence
                 // dereference nextRR before calling the function
                 nextRR = rr->next;
-                if (rr->SendRNow == mDNSInterfaceMark)
+                if (rr->SendRNow == uDNSInterfaceMark)
                 {
                     // Any records marked for sending should be eligible to be sent out
                     // immediately. Just being cautious
                 {
                     // Any records marked for sending should be eligible to be sent out
                     // immediately. Just being cautious
@@ -3160,14 +3449,13 @@ mDNSlocal void hndlRecordUpdateReply(mDNS *m, AuthRecord *rr, mStatus err, mDNSu
     mDNSBool InvokeCallback = mDNStrue;
     mDNSIPPort UpdatePort = zeroIPPort;
 
     mDNSBool InvokeCallback = mDNStrue;
     mDNSIPPort UpdatePort = zeroIPPort;
 
-    if (m->mDNS_busy != m->mDNS_reentrancy+1)
-        LogMsg("hndlRecordUpdateReply: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+    mDNS_CheckLock(m);
 
     LogInfo("hndlRecordUpdateReply: err %d ID %d state %d %s(%p)", err, mDNSVal16(rr->updateid), rr->state, ARDisplayString(m, rr), rr);
 
     rr->updateError = err;
 #if APPLE_OSX_mDNSResponder
 
     LogInfo("hndlRecordUpdateReply: err %d ID %d state %d %s(%p)", err, mDNSVal16(rr->updateid), rr->state, ARDisplayString(m, rr), rr);
 
     rr->updateError = err;
 #if APPLE_OSX_mDNSResponder
-    if (err == mStatus_BadSig || err == mStatus_BadKey) UpdateAutoTunnelDomainStatuses(m);
+    if (err == mStatus_BadSig || err == mStatus_BadKey || err == mStatus_BadTime) UpdateAutoTunnelDomainStatuses(m);
 #endif
 
     SetRecordRetry(m, rr, random);
 #endif
 
     SetRecordRetry(m, rr, random);
@@ -3298,19 +3586,45 @@ mDNSlocal void hndlRecordUpdateReply(mDNS *m, AuthRecord *rr, mStatus err, mDNSu
     // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
 }
 
     // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
 }
 
-mDNSexport void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *pkt, mDNSu16 len)
+mDNSlocal void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *pkt, mDNSu16 len)
 {
     NATTraversalInfo *ptr;
     NATAddrReply     *AddrReply    = (NATAddrReply    *)pkt;
     NATPortMapReply  *PortMapReply = (NATPortMapReply *)pkt;
     mDNSu32 nat_elapsed, our_elapsed;
 
 {
     NATTraversalInfo *ptr;
     NATAddrReply     *AddrReply    = (NATAddrReply    *)pkt;
     NATPortMapReply  *PortMapReply = (NATPortMapReply *)pkt;
     mDNSu32 nat_elapsed, our_elapsed;
 
-    // Minimum packet is vers (1) opcode (1) err (2) upseconds (4) = 8 bytes
-    if (!AddrReply->err && len < 8) { LogMsg("NAT Traversal message too short (%d bytes)", len); return; }
-    if (AddrReply->vers != NATMAP_VERS) { LogMsg("Received NAT Traversal response with version %d (expected %d)", pkt[0], NATMAP_VERS); return; }
+    // Minimum NAT-PMP packet is vers (1) opcode (1) + err (2) = 4 bytes
+    if (len < 4) { LogMsg("NAT-PMP message too short (%d bytes)", len); return; }
+    
+    // Read multi-byte error value (field is identical in a NATPortMapReply)
+    AddrReply->err = (mDNSu16) ((mDNSu16)pkt[2] << 8 | pkt[3]);
+    
+    if (AddrReply->err == NATErr_Vers)
+    {
+        NATTraversalInfo *n;        
+        LogInfo("NAT-PMP version unsupported message received");
+        for (n = m->NATTraversals; n; n=n->next)
+        {
+            // Send a NAT-PMP request for this operation as needed
+            // and update the state variables
+            uDNS_SendNATMsg(m, n, mDNSfalse);
+        }
+        
+        m->NextScheduledNATOp = m->timenow;
+
+        return;
+    }
 
 
-    // Read multi-byte numeric values (fields are identical in a NATPortMapReply)
-    AddrReply->err       = (mDNSu16) (                                                (mDNSu16)pkt[2] << 8 | pkt[3]);
+    // The minimum reasonable NAT-PMP packet length is vers (1) + opcode (1) + err (2) + upseconds (4) = 8 bytes
+    // If it's not at least this long, bail before we byte-swap the upseconds field & overrun our buffer.
+    // The retry timer will ensure we converge to correctness.
+    if (len < 8)
+    {
+        LogMsg("NAT-PMP message too short (%d bytes) 0x%X 0x%X", len, AddrReply->opcode, AddrReply->err);
+        return;
+    }
+    
+    // Read multi-byte upseconds value (field is identical in a NATPortMapReply)
     AddrReply->upseconds = (mDNSs32) ((mDNSs32)pkt[4] << 24 | (mDNSs32)pkt[5] << 16 | (mDNSs32)pkt[6] << 8 | pkt[7]);
 
     nat_elapsed = AddrReply->upseconds - m->LastNATupseconds;
     AddrReply->upseconds = (mDNSs32) ((mDNSs32)pkt[4] << 24 | (mDNSs32)pkt[5] << 16 | (mDNSs32)pkt[6] << 8 | pkt[7]);
 
     nat_elapsed = AddrReply->upseconds - m->LastNATupseconds;
@@ -3328,7 +3642,7 @@ mDNSexport void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID Interfac
     //       giving an apparent local time difference of 7 seconds
     //    The two-second safety margin coves this possible calculation discrepancy
     if (AddrReply->upseconds < m->LastNATupseconds || nat_elapsed + 2 < our_elapsed - our_elapsed/8)
     //       giving an apparent local time difference of 7 seconds
     //    The two-second safety margin coves this possible calculation discrepancy
     if (AddrReply->upseconds < m->LastNATupseconds || nat_elapsed + 2 < our_elapsed - our_elapsed/8)
-    { LogMsg("NAT gateway %#a rebooted", &m->Router); RecreateNATMappings(m); }
+    { LogMsg("NAT-PMP epoch time check failed: assuming NAT gateway %#a rebooted", &m->Router); RecreateNATMappings(m, 0); }
 
     m->LastNATupseconds      = AddrReply->upseconds;
     m->LastNATReplyLocalTime = m->timenow;
 
     m->LastNATupseconds      = AddrReply->upseconds;
     m->LastNATReplyLocalTime = m->timenow;
@@ -3343,7 +3657,7 @@ mDNSexport void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID Interfac
         mDNS_snprintf(msgbuf, sizeof(msgbuf), "%d", AddrReply->err);
         mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.natpmp.AddressRequest", AddrReply->err ? "failure" : "success", msgbuf, "");
 #endif
         mDNS_snprintf(msgbuf, sizeof(msgbuf), "%d", AddrReply->err);
         mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.natpmp.AddressRequest", AddrReply->err ? "failure" : "success", msgbuf, "");
 #endif
-        if (!AddrReply->err && len < sizeof(NATAddrReply)) { LogMsg("NAT Traversal AddrResponse message too short (%d bytes)", len); return; }
+        if (!AddrReply->err && len < sizeof(NATAddrReply)) { LogMsg("NAT-PMP AddrResponse message too short (%d bytes)", len); return; }
         natTraversalHandleAddressReply(m, AddrReply->err, AddrReply->ExtAddr);
     }
     else if (AddrReply->opcode == NATOp_MapUDPResponse || AddrReply->opcode == NATOp_MapTCPResponse)
         natTraversalHandleAddressReply(m, AddrReply->err, AddrReply->ExtAddr);
     }
     else if (AddrReply->opcode == NATOp_MapUDPResponse || AddrReply->opcode == NATOp_MapTCPResponse)
@@ -3356,7 +3670,7 @@ mDNSexport void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID Interfac
 #endif
         if (!PortMapReply->err)
         {
 #endif
         if (!PortMapReply->err)
         {
-            if (len < sizeof(NATPortMapReply)) { LogMsg("NAT Traversal PortMapReply message too short (%d bytes)", len); return; }
+            if (len < sizeof(NATPortMapReply)) { LogMsg("NAT-PMP PortMapReply message too short (%d bytes)", len); return; }
             PortMapReply->NATRep_lease = (mDNSu32) ((mDNSu32)pkt[12] << 24 | (mDNSu32)pkt[13] << 16 | (mDNSu32)pkt[14] << 8 | pkt[15]);
         }
 
             PortMapReply->NATRep_lease = (mDNSu32) ((mDNSu32)pkt[12] << 24 | (mDNSu32)pkt[13] << 16 | (mDNSu32)pkt[14] << 8 | pkt[15]);
         }
 
@@ -3367,14 +3681,157 @@ mDNSexport void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID Interfac
 
         for (ptr = m->NATTraversals; ptr; ptr=ptr->next)
             if (ptr->Protocol == Protocol && mDNSSameIPPort(ptr->IntPort, PortMapReply->intport))
 
         for (ptr = m->NATTraversals; ptr; ptr=ptr->next)
             if (ptr->Protocol == Protocol && mDNSSameIPPort(ptr->IntPort, PortMapReply->intport))
-                natTraversalHandlePortMapReply(m, ptr, InterfaceID, PortMapReply->err, PortMapReply->extport, PortMapReply->NATRep_lease);
+                natTraversalHandlePortMapReply(m, ptr, InterfaceID, PortMapReply->err, PortMapReply->extport, PortMapReply->NATRep_lease, NATTProtocolNATPMP);
     }
     }
-    else { LogMsg("Received NAT Traversal response with version unknown opcode 0x%X", AddrReply->opcode); return; }
+    else { LogMsg("Received NAT-PMP response with unknown opcode 0x%X", AddrReply->opcode); return; }
 
     // Don't need an SSDP socket if we get a NAT-PMP packet
     if (m->SSDPSocket) { debugf("uDNS_ReceiveNATPMPPacket destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; }
 }
 
 
     // Don't need an SSDP socket if we get a NAT-PMP packet
     if (m->SSDPSocket) { debugf("uDNS_ReceiveNATPMPPacket destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; }
 }
 
+mDNSlocal void uDNS_ReceivePCPPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *pkt, mDNSu16 len)
+{
+    NATTraversalInfo *ptr;
+    PCPMapReply *reply = (PCPMapReply*)pkt;
+    mDNSu32 client_delta, server_delta;
+    mDNSBool checkEpochValidity = m->LastNATupseconds != 0;
+    mDNSu8 strippedOpCode;
+    mDNSv4Addr mappedAddress = zerov4Addr;
+    mDNSu8 protocol = 0;
+    mDNSIPPort intport = zeroIPPort;
+    mDNSIPPort extport = zeroIPPort;
+    
+    // Minimum PCP packet is 24 bytes
+    if (len < 24)
+    {
+        LogMsg("uDNS_ReceivePCPPacket: message too short (%d bytes)", len);
+        return;
+    }
+    
+    strippedOpCode = reply->opCode & 0x7f;
+    
+    if ((reply->opCode & 0x80) == 0x00 || (strippedOpCode != PCPOp_Announce && strippedOpCode != PCPOp_Map))
+    {
+        LogMsg("uDNS_ReceivePCPPacket: unhandled opCode %u", reply->opCode);
+        return;
+    }
+
+    // Read multi-byte values
+    reply->lifetime = (mDNSs32)((mDNSs32)pkt[4] << 24 | (mDNSs32)pkt[5] << 16 | (mDNSs32)pkt[ 6] << 8 | pkt[ 7]);
+    reply->epoch    = (mDNSs32)((mDNSs32)pkt[8] << 24 | (mDNSs32)pkt[9] << 16 | (mDNSs32)pkt[10] << 8 | pkt[11]);
+
+    client_delta = (m->timenow - m->LastNATReplyLocalTime) / mDNSPlatformOneSecond;
+    server_delta = reply->epoch - m->LastNATupseconds;
+    debugf("uDNS_ReceivePCPPacket: %X %X upseconds %u client_delta %d server_delta %d", reply->opCode, reply->result, reply->epoch, client_delta, server_delta);
+
+    // If seconds since the epoch is 0, use 1 so we'll check epoch validity next time
+    m->LastNATupseconds      = reply->epoch ? reply->epoch : 1;
+    m->LastNATReplyLocalTime = m->timenow;
+
+#ifdef _LEGACY_NAT_TRAVERSAL_
+    LNT_ClearState(m);
+#endif // _LEGACY_NAT_TRAVERSAL_
+
+    // Don't need an SSDP socket if we get a PCP packet
+    if (m->SSDPSocket) { debugf("uDNS_ReceivePCPPacket: destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; }
+
+    if (checkEpochValidity && (client_delta + 2 < server_delta - server_delta / 16 || server_delta + 2 < client_delta - client_delta / 16))
+    {
+        // If this is an ANNOUNCE packet, wait a random interval up to 5 seconds
+        // otherwise, refresh immediately
+        mDNSu32 waitTicks = strippedOpCode ? 0 : mDNSRandom(PCP_WAITSECS_AFTER_EPOCH_INVALID * mDNSPlatformOneSecond);
+        LogMsg("uDNS_ReceivePCPPacket: Epoch invalid, %#a likely rebooted, waiting %u ticks", &m->Router, waitTicks);
+        RecreateNATMappings(m, waitTicks);
+        // we can ignore the rest of this packet, as new requests are about to go out
+        return;
+    }
+
+    if (strippedOpCode == PCPOp_Announce)
+        return;
+    
+    // We globally keep track of the most recent error code for mappings.
+    // This seems bad to do with PCP, but best not change it now.
+    m->LastNATMapResultCode = reply->result;
+    
+    if (!reply->result)
+    {
+        if (len < sizeof(PCPMapReply))
+        {
+            LogMsg("uDNS_ReceivePCPPacket: mapping response too short (%d bytes)", len);
+            return;
+        }
+        
+        // Check the nonce
+        if (reply->nonce[0] != m->PCPNonce[0] || reply->nonce[1] != m->PCPNonce[1] || reply->nonce[2] != m->PCPNonce[2])
+        {
+            LogMsg("uDNS_ReceivePCPPacket: invalid nonce, ignoring. received { %x %x %x } expected { %x %x %x }",
+                   reply->nonce[0], reply->nonce[1], reply->nonce[2],
+                    m->PCPNonce[0],  m->PCPNonce[1],  m->PCPNonce[2]);
+            return;
+        }
+
+        // Get the values
+        protocol = reply->protocol;
+        intport = reply->intPort;
+        extport = reply->extPort;
+
+        // Get the external address, which should be mapped, since we only support IPv4
+        if (!mDNSAddrIPv4FromMappedIPv6(&reply->extAddress, &mappedAddress))
+        {
+            LogMsg("uDNS_ReceivePCPPacket: unexpected external address: %.16a", &reply->extAddress);
+            reply->result = NATErr_NetFail;
+            // fall through to report the error
+        }
+        else if (mDNSIPv4AddressIsZero(mappedAddress))
+        {
+            // If this is the deletion case, we will have sent the zero IPv4-mapped address
+            // in our request, and the server should reflect it in the response, so we
+            // should not log about receiving a zero address. And in this case, we no
+            // longer have a NATTraversal to report errors back to, so it's ok to set the
+            // result here.
+            // In other cases, a zero address is an error, and we will have a NATTraversal
+            // to report back to, so set an error and fall through to report it.
+            // CheckNATMappings will log the error.
+            reply->result = NATErr_NetFail;
+        }
+    }
+    else
+    {
+        LogInfo("uDNS_ReceivePCPPacket: error received from server. opcode %X result %X lifetime %X epoch %X",
+                reply->opCode, reply->result, reply->lifetime, reply->epoch);
+        
+        // If the packet is long enough, get the protocol & intport for matching to report
+        // the error
+        if (len >= sizeof(PCPMapReply))
+        {
+            protocol = reply->protocol;
+            intport = reply->intPort;
+        }
+    }
+
+    for (ptr = m->NATTraversals; ptr; ptr=ptr->next)
+    {
+        mDNSu8 ptrProtocol = ((ptr->Protocol & NATOp_MapTCP) == NATOp_MapTCP ? PCPProto_TCP : PCPProto_UDP);
+        if ((protocol == ptrProtocol && mDNSSameIPPort(ptr->IntPort, intport)) ||
+            (!ptr->Protocol && protocol == PCPProto_TCP && mDNSSameIPPort(DiscardPort, intport)))
+        {
+            natTraversalHandlePortMapReplyWithAddress(m, ptr, InterfaceID, reply->result ? NATErr_NetFail : NATErr_None, mappedAddress, extport, reply->lifetime, NATTProtocolPCP);
+        }
+    }
+}
+
+mDNSexport void uDNS_ReceiveNATPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *pkt, mDNSu16 len)
+{
+    if (len == 0)
+        LogMsg("uDNS_ReceiveNATPacket: zero length packet");
+    else if (pkt[0] == PCP_VERS)
+        uDNS_ReceivePCPPacket(m, InterfaceID, pkt, len);
+    else if (pkt[0] == NATMAP_VERS)
+        uDNS_ReceiveNATPMPPacket(m, InterfaceID, pkt, len);
+    else
+        LogMsg("uDNS_ReceiveNATPacket: packet with version %u (expected %u or %u)", pkt[0], PCP_VERS, NATMAP_VERS);
+}
+
 // <rdar://problem/3925163> Shorten DNS-SD queries to avoid NAT bugs
 // <rdar://problem/4288449> Add check to avoid crashing NAT gateways that have buggy DNS relay code
 //
 // <rdar://problem/3925163> Shorten DNS-SD queries to avoid NAT bugs
 // <rdar://problem/4288449> Add check to avoid crashing NAT gateways that have buggy DNS relay code
 //
@@ -3622,7 +4079,19 @@ mDNSexport void sendLLQRefresh(mDNS *m, DNSQuestion *q)
     if (PrivateQuery(q) && !q->tcp)
     {
         LogInfo("sendLLQRefresh setting up new TLS session %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
     if (PrivateQuery(q) && !q->tcp)
     {
         LogInfo("sendLLQRefresh setting up new TLS session %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
-        if (!q->nta) { LogMsg("sendLLQRefresh:ERROR!! q->nta is NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; }
+        if (!q->nta)
+        {
+            // Note: If a question is in LLQ_Established state, we never free the zone data for the
+            // question (PrivateQuery). If we free, we reset the state to something other than LLQ_Established.
+            // This function is called only if the query is in LLQ_Established state and hence nta should
+            // never be NULL. In spite of that, we have seen q->nta being NULL in the field. Just refetch the
+            // zone data in that case.
+            q->nta = StartGetZoneData(m, &q->qname, ZoneServiceLLQ, LLQGotZoneData, q);
+            return;
+            // ThisQInterval is not adjusted when we return from here which means that we will get called back
+            // again immediately. As q->servAddr and q->servPort are still valid and the nta->Host is initialized
+            // without any additional discovery for PrivateQuery, things work. 
+        }
         q->tcp = MakeTCPConn(m, &m->omsg, end, kTCPSocketFlags_UseTLS, &q->servAddr, q->servPort, &q->nta->Host, q, mDNSNULL);
     }
     else
         q->tcp = MakeTCPConn(m, &m->omsg, end, kTCPSocketFlags_UseTLS, &q->servAddr, q->servPort, &q->nta->Host, q, mDNSNULL);
     }
     else
@@ -3928,8 +4397,7 @@ mDNSlocal void SendRecordDeregistration(mDNS *m, AuthRecord *rr)
     mDNSu8 *limit;
     DomainAuthInfo *AuthInfo;
 
     mDNSu8 *limit;
     DomainAuthInfo *AuthInfo;
 
-    if (m->mDNS_busy != m->mDNS_reentrancy+1)
-        LogMsg("SendRecordDeRegistration: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+    mDNS_CheckLock(m);
 
     if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4))
     {
 
     if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4))
     {
@@ -3993,7 +4461,7 @@ mDNSexport mStatus uDNS_DeregisterRecord(mDNS *const m, AuthRecord *const rr)
     case regState_NATMap:
     // A record could be in NoTarget to start with if the corresponding SRV record could not find a target.
     // It is also possible to reenter the NoTarget state when we move to a network with a NAT that has
     case regState_NATMap:
     // A record could be in NoTarget to start with if the corresponding SRV record could not find a target.
     // It is also possible to reenter the NoTarget state when we move to a network with a NAT that has
-    // no NAT-PMP/UPnP support. In that case before we entered NoTarget, we already deregistered with
+    // no {PCP, NAT-PMP, UPnP/IGD} support. In that case before we entered NoTarget, we already deregistered with
     // the server.
     case regState_NoTarget:
     case regState_Unregistered:
     // the server.
     case regState_NoTarget:
     case regState_Unregistered:
@@ -4160,6 +4628,47 @@ unreg_error:
 #pragma mark - Periodic Execution Routines
 #endif
 
 #pragma mark - Periodic Execution Routines
 #endif
 
+mDNSlocal void handle_unanswered_query(mDNS *const m)
+{
+    DNSQuestion *q = m->CurrentQuestion;
+
+    if (q->unansweredQueries >= MAX_DNSSEC_UNANSWERED_QUERIES && DNSSECOptionalQuestion(q))
+    {
+        // If we are not receiving any responses for DNSSEC question, it could be due to
+        // a broken middlebox or a DNS server that does not understand the EDNS0/DOK option that
+        // silently drops the packets. Also as per RFC 5625 there are certain buggy DNS Proxies
+        // that are known to drop these pkts. To handle this, we turn off sending the EDNS0/DOK
+        // option if we have not received any responses indicating that the server or
+        // the middlebox is DNSSEC aware. If we receive at least one response to a DNSSEC
+        // question, we don't turn off validation. Also, we wait for MAX_DNSSEC_RETRANSMISSIONS
+        // before turning off validation to accomodate packet loss.
+        // 
+        // Note: req_DO affects only DNSSEC_VALIDATION_SECURE_OPTIONAL questions;
+        // DNSSEC_VALIDATION_SECURE questions ignores req_DO.
+
+        if (q->qDNSServer && !q->qDNSServer->DNSSECAware && q->qDNSServer->req_DO)
+        {
+            q->qDNSServer->retransDO++;
+            if (q->qDNSServer->retransDO == MAX_DNSSEC_RETRANSMISSIONS)
+            {
+                LogInfo("handle_unanswered_query: setting req_DO false for %#a", &q->qDNSServer->addr);
+                q->qDNSServer->req_DO = mDNSfalse;
+            }
+        }
+
+        if (!q->qDNSServer->req_DO)
+        {
+            q->ValidationState     = DNSSECValNotRequired; 
+            q->ValidationRequired  = DNSSEC_VALIDATION_NONE;
+        
+            if (q->ProxyQuestion)
+                q->ProxyDNSSECOK = mDNSfalse;
+            LogInfo("handle_unanswered_query: unanswered query for %##s (%s), so turned off validation for %#a", 
+                q->qname.c, DNSTypeName(q->qtype), &q->qDNSServer->addr);
+        }
+    }
+}
+
 // The question to be checked is not passed in as an explicit parameter;
 // instead it is implicit that the question to be checked is m->CurrentQuestion.
 mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m)
 // The question to be checked is not passed in as an explicit parameter;
 // instead it is implicit that the question to be checked is m->CurrentQuestion.
 mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m)
@@ -4184,6 +4693,7 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m)
         }
     }
 
         }
     }
 
+    handle_unanswered_query(m);  
     // We repeat the check above (rather than just making this the "else" case) because startLLQHandshake can change q->state to LLQ_Poll
     if (!(q->LongLived && q->state != LLQ_Poll))
     {
     // We repeat the check above (rather than just making this the "else" case) because startLLQHandshake can change q->state to LLQ_Poll
     if (!(q->LongLived && q->state != LLQ_Poll))
     {
@@ -4194,7 +4704,7 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m)
                 LogInfo("uDNS_CheckCurrentQuestion: Sent %d unanswered queries for %##s (%s) to %#a:%d (%##s)",
                         q->unansweredQueries, q->qname.c, DNSTypeName(q->qtype), &orig->addr, mDNSVal16(orig->port), orig->domain.c);
 
                 LogInfo("uDNS_CheckCurrentQuestion: Sent %d unanswered queries for %##s (%s) to %#a:%d (%##s)",
                         q->unansweredQueries, q->qname.c, DNSTypeName(q->qtype), &orig->addr, mDNSVal16(orig->port), orig->domain.c);
 
-            PenalizeDNSServer(m, q);
+            PenalizeDNSServer(m, q, zeroID);
             q->noServerResponse = 1;
         }
         // There are two cases here.
             q->noServerResponse = 1;
         }
         // There are two cases here.
@@ -4240,7 +4750,12 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m)
             {
                 end = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass);
                 if (DNSSECQuestion(q) && !q->qDNSServer->cellIntf)
             {
                 end = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass);
                 if (DNSSECQuestion(q) && !q->qDNSServer->cellIntf)
-                    end = putDNSSECOption(&m->omsg, end, m->omsg.data + AbsoluteMaxDNSMessageData);
+                {
+                    if (q->ProxyQuestion)
+                        end = DNSProxySetAttributes(q, &m->omsg.h, &m->omsg, end, m->omsg.data + AbsoluteMaxDNSMessageData);
+                    else
+                        end = putDNSSECOption(&m->omsg, end, m->omsg.data + AbsoluteMaxDNSMessageData);
+                }
                 private = PrivateQuery(q);
             }
             else if (m->timenow - q->qDNSServer->lasttest >= INIT_UCAST_POLL_INTERVAL)  // Make sure at least three seconds has elapsed since last test query
                 private = PrivateQuery(q);
             }
             else if (m->timenow - q->qDNSServer->lasttest >= INIT_UCAST_POLL_INTERVAL)  // Make sure at least three seconds has elapsed since last test query
@@ -4266,9 +4781,14 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m)
                     debugf("uDNS_CheckCurrentQuestion sending %p %##s (%s) %#a:%d UnansweredQueries %d",
                            q, q->qname.c, DNSTypeName(q->qtype),
                            q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort), q->unansweredQueries);
                     debugf("uDNS_CheckCurrentQuestion sending %p %##s (%s) %#a:%d UnansweredQueries %d",
                            q, q->qname.c, DNSTypeName(q->qtype),
                            q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort), q->unansweredQueries);
-                    if (!q->LocalSocket) q->LocalSocket = mDNSPlatformUDPSocket(m, zeroIPPort);
+                    if (!q->LocalSocket)
+                    {
+                        q->LocalSocket = mDNSPlatformUDPSocket(m, zeroIPPort);
+                        if (q->LocalSocket)
+                            mDNSPlatformSetDelegatePID(q->LocalSocket, &q->qDNSServer->addr, q);
+                    }
                     if (!q->LocalSocket) err = mStatus_NoMemoryErr; // If failed to make socket (should be very rare), we'll try again next time
                     if (!q->LocalSocket) err = mStatus_NoMemoryErr; // If failed to make socket (should be very rare), we'll try again next time
-                    else err = mDNSSendDNSMessage(m, &m->omsg, end, q->qDNSServer->interface, q->LocalSocket, &q->qDNSServer->addr, q->qDNSServer->port, mDNSNULL, mDNSNULL, q->UseBrackgroundTrafficClass);
+                    else err = mDNSSendDNSMessage(m, &m->omsg, end, q->qDNSServer->interface, q->LocalSocket, &q->qDNSServer->addr, q->qDNSServer->port, mDNSNULL, mDNSNULL, q->UseBackgroundTrafficClass);
                 }
             }
 
                 }
             }
 
@@ -4310,13 +4830,9 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m)
             // (When we have a group of identical questions, only the active representative of the group gets
             // passed to uDNS_CheckCurrentQuestion -- we only want one set of query packets hitting the wire --
             // but we want *all* of the questions to get answer callbacks.)
             // (When we have a group of identical questions, only the active representative of the group gets
             // passed to uDNS_CheckCurrentQuestion -- we only want one set of query packets hitting the wire --
             // but we want *all* of the questions to get answer callbacks.)
-
             CacheRecord *rr;
             const mDNSu32 slot = HashSlot(&q->qname);
             CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
             CacheRecord *rr;
             const mDNSu32 slot = HashSlot(&q->qname);
             CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
-            if (cg)
-                for (rr = cg->members; rr; rr=rr->next)
-                    if (SameNameRecordAnswersQuestion(&rr->resrec, q)) mDNS_PurgeCacheResourceRecord(m, rr);
 
             if (!q->qDNSServer)
             {
 
             if (!q->qDNSServer)
             {
@@ -4358,16 +4874,30 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m)
                 LogMsg("uDNS_CheckCurrentQuestion DNS server %#a:%d for %##s is disabled", &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qname.c);
             }
 
                 LogMsg("uDNS_CheckCurrentQuestion DNS server %#a:%d for %##s is disabled", &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qname.c);
             }
 
+            if (cg)
+            {
+                for (rr = cg->members; rr; rr=rr->next)
+                {
+                    if (SameNameRecordAnswersQuestion(&rr->resrec, q))
+                    {
+                        LogInfo("uDNS_CheckCurrentQuestion: Purged resourcerecord %s", CRDisplayString(m, rr)); 
+                        mDNS_PurgeCacheResourceRecord(m, rr);
+                    }
+                }
+            }
             // For some of the WAB queries that we generate form within the mDNSResponder, most of the home routers
             // don't understand and return ServFail/NXDomain. In those cases, we don't want to try too often. We try
             // every fifteen minutes in that case
             MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, (DomainEnumQuery(&q->qname) ? 60 * 15 : 60), mDNSInterface_Any, q->qDNSServer);
             q->unansweredQueries = 0;
             // For some of the WAB queries that we generate form within the mDNSResponder, most of the home routers
             // don't understand and return ServFail/NXDomain. In those cases, we don't want to try too often. We try
             // every fifteen minutes in that case
             MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, (DomainEnumQuery(&q->qname) ? 60 * 15 : 60), mDNSInterface_Any, q->qDNSServer);
             q->unansweredQueries = 0;
+            if (!mDNSOpaque16IsZero(q->responseFlags))
+                m->rec.r.responseFlags = q->responseFlags;
             // We're already using the m->CurrentQuestion pointer, so CacheRecordAdd can't use it to walk the question list.
             // To solve this problem we set rr->DelayDelivery to a nonzero value (which happens to be 'now') so that we
             // momentarily defer generating answer callbacks until mDNS_Execute time.
             CreateNewCacheEntry(m, slot, cg, NonZeroTime(m->timenow), mDNStrue, mDNSNULL);
             ScheduleNextCacheCheckTime(m, slot, NonZeroTime(m->timenow));
             // We're already using the m->CurrentQuestion pointer, so CacheRecordAdd can't use it to walk the question list.
             // To solve this problem we set rr->DelayDelivery to a nonzero value (which happens to be 'now') so that we
             // momentarily defer generating answer callbacks until mDNS_Execute time.
             CreateNewCacheEntry(m, slot, cg, NonZeroTime(m->timenow), mDNStrue, mDNSNULL);
             ScheduleNextCacheCheckTime(m, slot, NonZeroTime(m->timenow));
+            m->rec.r.responseFlags = zeroID;
             m->rec.r.resrec.RecordType = 0;     // Clear RecordType to show we're not still using it
             // MUST NOT touch m->CurrentQuestion (or q) after this -- client callback could have deleted it
         }
             m->rec.r.resrec.RecordType = 0;     // Clear RecordType to show we're not still using it
             // MUST NOT touch m->CurrentQuestion (or q) after this -- client callback could have deleted it
         }
@@ -4381,9 +4911,9 @@ mDNSexport void CheckNATMappings(mDNS *m)
     mDNSBool HaveRoutable = !rfc1918 && !mDNSIPv4AddressIsZero(m->AdvertisedV4.ip.v4);
     m->NextScheduledNATOp = m->timenow + 0x3FFFFFFF;
 
     mDNSBool HaveRoutable = !rfc1918 && !mDNSIPv4AddressIsZero(m->AdvertisedV4.ip.v4);
     m->NextScheduledNATOp = m->timenow + 0x3FFFFFFF;
 
-    if (HaveRoutable) m->ExternalAddress = m->AdvertisedV4.ip.v4;
+    if (HaveRoutable) m->ExtAddress = m->AdvertisedV4.ip.v4;
 
 
-    if (m->NATTraversals && rfc1918)            // Do we need to open NAT-PMP socket to receive multicast announcements from router?
+    if (m->NATTraversals && rfc1918)            // Do we need to open a socket to receive multicast announcements from router?
     {
         if (m->NATMcastRecvskt == mDNSNULL)     // If we are behind a NAT and the socket hasn't been opened yet, open it
         {
     {
         if (m->NATMcastRecvskt == mDNSNULL)     // If we are behind a NAT and the socket hasn't been opened yet, open it
         {
@@ -4394,7 +4924,7 @@ mDNSexport void CheckNATMappings(mDNS *m)
             {
                 if (needLog)
                 {
             {
                 if (needLog)
                 {
-                    LogMsg("CheckNATMappings: Failed to allocate port 5350 UDP multicast socket for NAT-PMP announcements");
+                    LogMsg("CheckNATMappings: Failed to allocate port 5350 UDP multicast socket for PCP & NAT-PMP announcements");
                     needLog = mDNSfalse;
                 }
             }
                     needLog = mDNSfalse;
                 }
             }
@@ -4408,29 +4938,7 @@ mDNSexport void CheckNATMappings(mDNS *m)
         if (m->SSDPSocket)      { debugf("CheckNATMappings destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; }
     }
 
         if (m->SSDPSocket)      { debugf("CheckNATMappings destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; }
     }
 
-    if (!m->NATTraversals)
-        m->retryGetAddr = m->timenow + 0x78000000;
-    else
-    {
-        if (m->timenow - m->retryGetAddr >= 0)
-        {
-            err = uDNS_SendNATMsg(m, mDNSNULL);     // Will also do UPnP discovery for us, if necessary
-            if (!err)
-            {
-                if      (m->retryIntervalGetAddr < NATMAP_INIT_RETRY) m->retryIntervalGetAddr = NATMAP_INIT_RETRY;
-                else if (m->retryIntervalGetAddr < NATMAP_MAX_RETRY_INTERVAL / 2) m->retryIntervalGetAddr *= 2;
-                else m->retryIntervalGetAddr = NATMAP_MAX_RETRY_INTERVAL;
-            }
-            LogInfo("CheckNATMappings retryGetAddr sent address request err %d interval %d", err, m->retryIntervalGetAddr);
-
-            // Always update m->retryGetAddr, even if we fail to send the packet. Otherwise in cases where we can't send the packet
-            // (like when we have no active interfaces) we'll spin in an infinite loop repeatedly failing to send the packet
-            m->retryGetAddr = m->timenow + m->retryIntervalGetAddr;
-        }
-        // Even when we didn't send the GetAddr packet, still need to make sure NextScheduledNATOp is set correctly
-        if (m->NextScheduledNATOp - m->retryGetAddr > 0)
-            m->NextScheduledNATOp = m->retryGetAddr;
-    }
+    uDNS_RequestAddress(m);
 
     if (m->CurrentNATTraversal) LogMsg("WARNING m->CurrentNATTraversal already in use");
     m->CurrentNATTraversal = m->NATTraversals;
 
     if (m->CurrentNATTraversal) LogMsg("WARNING m->CurrentNATTraversal already in use");
     m->CurrentNATTraversal = m->NATTraversals;
@@ -4438,6 +4946,7 @@ mDNSexport void CheckNATMappings(mDNS *m)
     while (m->CurrentNATTraversal)
     {
         NATTraversalInfo *cur = m->CurrentNATTraversal;
     while (m->CurrentNATTraversal)
     {
         NATTraversalInfo *cur = m->CurrentNATTraversal;
+        mDNSv4Addr EffectiveAddress = HaveRoutable ? m->AdvertisedV4.ip.v4 : cur->NewAddress;
         m->CurrentNATTraversal = m->CurrentNATTraversal->next;
 
         if (HaveRoutable)       // If not RFC 1918 address, our own address and port are effectively our external address and port
         m->CurrentNATTraversal = m->CurrentNATTraversal->next;
 
         if (HaveRoutable)       // If not RFC 1918 address, our own address and port are effectively our external address and port
@@ -4445,9 +4954,9 @@ mDNSexport void CheckNATMappings(mDNS *m)
             cur->ExpiryTime = 0;
             cur->NewResult  = mStatus_NoError;
         }
             cur->ExpiryTime = 0;
             cur->NewResult  = mStatus_NoError;
         }
-        else if (cur->Protocol)     // Check if it's time to send port mapping packets
+        else // Check if it's time to send port mapping packet(s)
         {
         {
-            if (m->timenow - cur->retryPortMap >= 0)                        // Time to do something with this mapping
+            if (m->timenow - cur->retryPortMap >= 0) // Time to send a mapping request for this packet
             {
                 if (cur->ExpiryTime && cur->ExpiryTime - m->timenow < 0)    // Mapping has expired
                 {
             {
                 if (cur->ExpiryTime && cur->ExpiryTime - m->timenow < 0)    // Mapping has expired
                 {
@@ -4455,8 +4964,7 @@ mDNSexport void CheckNATMappings(mDNS *m)
                     cur->retryInterval = NATMAP_INIT_RETRY;
                 }
 
                     cur->retryInterval = NATMAP_INIT_RETRY;
                 }
 
-                //LogMsg("uDNS_SendNATMsg");
-                err = uDNS_SendNATMsg(m, cur);
+                err = uDNS_SendNATMsg(m, cur, mDNStrue); // Will also do UPnP discovery for us, if necessary
 
                 if (cur->ExpiryTime)                        // If have active mapping then set next renewal time halfway to expiry
                     NATSetNextRenewalTime(m, cur);
 
                 if (cur->ExpiryTime)                        // If have active mapping then set next renewal time halfway to expiry
                     NATSetNextRenewalTime(m, cur);
@@ -4470,24 +4978,35 @@ mDNSexport void CheckNATMappings(mDNS *m)
             }
 
             if (m->NextScheduledNATOp - cur->retryPortMap > 0)
             }
 
             if (m->NextScheduledNATOp - cur->retryPortMap > 0)
+            {
                 m->NextScheduledNATOp = cur->retryPortMap;
                 m->NextScheduledNATOp = cur->retryPortMap;
+            }
         }
 
         // Notify the client if necessary. We invoke the callback if:
         }
 
         // Notify the client if necessary. We invoke the callback if:
-        // (1) we have an ExternalAddress, or we've tried and failed a couple of times to discover it
-        // and (2) the client doesn't want a mapping, or the client won't need a mapping, or the client has a successful mapping, or we've tried and failed a couple of times
-        // and (3) we have new data to give the client that's changed since the last callback
+        // (1) We have an effective address,
+        //     or we've tried and failed a couple of times to discover it
+        // AND
+        // (2) the client requested the address only,
+        //     or the client won't need a mapping because we have a routable address,
+        //     or the client has an expiry time and therefore a successful mapping,
+        //     or we've tried and failed a couple of times (see "Time line" below)
+        // AND
+        // (3) we have new data to give the client that's changed since the last callback
+        //
         // Time line is: Send, Wait 500ms, Send, Wait 1sec, Send, Wait 2sec, Send
         // At this point we've sent three requests without an answer, we've just sent our fourth request,
         // Time line is: Send, Wait 500ms, Send, Wait 1sec, Send, Wait 2sec, Send
         // At this point we've sent three requests without an answer, we've just sent our fourth request,
-        // retryIntervalGetAddr is now 4 seconds, which is greater than NATMAP_INIT_RETRY * 8 (2 seconds),
+        // retryInterval is now 4 seconds, which is greater than NATMAP_INIT_RETRY * 8 (2 seconds),
         // so we return an error result to the caller.
         // so we return an error result to the caller.
-        if (!mDNSIPv4AddressIsZero(m->ExternalAddress) || m->retryIntervalGetAddr > NATMAP_INIT_RETRY * 8)
+        if (!mDNSIPv4AddressIsZero(EffectiveAddress) || cur->retryInterval > NATMAP_INIT_RETRY * 8)
         {
         {
-            const mStatus EffectiveResult = cur->NewResult ? cur->NewResult : mDNSv4AddrIsRFC1918(&m->ExternalAddress) ? mStatus_DoubleNAT : mStatus_NoError;
+            const mStatus EffectiveResult = cur->NewResult ? cur->NewResult : mDNSv4AddrIsRFC1918(&EffectiveAddress) ? mStatus_DoubleNAT : mStatus_NoError;
             const mDNSIPPort ExternalPort = HaveRoutable ? cur->IntPort :
             const mDNSIPPort ExternalPort = HaveRoutable ? cur->IntPort :
-                                            !mDNSIPv4AddressIsZero(m->ExternalAddress) && cur->ExpiryTime ? cur->RequestedPort : zeroIPPort;
+                                            !mDNSIPv4AddressIsZero(EffectiveAddress) && cur->ExpiryTime ? cur->RequestedPort : zeroIPPort;
+                                            
             if (!cur->Protocol || HaveRoutable || cur->ExpiryTime || cur->retryInterval > NATMAP_INIT_RETRY * 8)
             if (!cur->Protocol || HaveRoutable || cur->ExpiryTime || cur->retryInterval > NATMAP_INIT_RETRY * 8)
-                if (!mDNSSameIPv4Address(cur->ExternalAddress, m->ExternalAddress) ||
+            {
+                if (!mDNSSameIPv4Address(cur->ExternalAddress, EffectiveAddress) ||
                     !mDNSSameIPPort     (cur->ExternalPort,       ExternalPort)    ||
                     cur->Result != EffectiveResult)
                 {
                     !mDNSSameIPPort     (cur->ExternalPort,       ExternalPort)    ||
                     cur->Result != EffectiveResult)
                 {
@@ -4496,13 +5015,13 @@ mDNSexport void CheckNATMappings(mDNS *m)
                     {
                         if (!EffectiveResult)
                             LogInfo("CheckNATMapping: Failed to obtain NAT port mapping %p from router %#a external address %.4a internal port %5d interval %d error %d",
                     {
                         if (!EffectiveResult)
                             LogInfo("CheckNATMapping: Failed to obtain NAT port mapping %p from router %#a external address %.4a internal port %5d interval %d error %d",
-                                    cur, &m->Router, &m->ExternalAddress, mDNSVal16(cur->IntPort), cur->retryInterval, EffectiveResult);
+                                    cur, &m->Router, &EffectiveAddress, mDNSVal16(cur->IntPort), cur->retryInterval, EffectiveResult);
                         else
                             LogMsg("CheckNATMapping: Failed to obtain NAT port mapping %p from router %#a external address %.4a internal port %5d interval %d error %d",
                         else
                             LogMsg("CheckNATMapping: Failed to obtain NAT port mapping %p from router %#a external address %.4a internal port %5d interval %d error %d",
-                                   cur, &m->Router, &m->ExternalAddress, mDNSVal16(cur->IntPort), cur->retryInterval, EffectiveResult);
+                                   cur, &m->Router, &EffectiveAddress, mDNSVal16(cur->IntPort), cur->retryInterval, EffectiveResult);
                     }
 
                     }
 
-                    cur->ExternalAddress = m->ExternalAddress;
+                    cur->ExternalAddress = EffectiveAddress;
                     cur->ExternalPort    = ExternalPort;
                     cur->Lifetime        = cur->ExpiryTime && !mDNSIPPortIsZero(ExternalPort) ?
                                            (cur->ExpiryTime - m->timenow + mDNSPlatformOneSecond/2) / mDNSPlatformOneSecond : 0;
                     cur->ExternalPort    = ExternalPort;
                     cur->Lifetime        = cur->ExpiryTime && !mDNSIPPortIsZero(ExternalPort) ?
                                            (cur->ExpiryTime - m->timenow + mDNSPlatformOneSecond/2) / mDNSPlatformOneSecond : 0;
@@ -4513,6 +5032,7 @@ mDNSexport void CheckNATMappings(mDNS *m)
                     mDNS_ReclaimLockAfterCallback();    // Decrement mDNS_reentrancy to block mDNS API calls again
                     // MUST NOT touch cur after invoking the callback
                 }
                     mDNS_ReclaimLockAfterCallback();    // Decrement mDNS_reentrancy to block mDNS API calls again
                     // MUST NOT touch cur after invoking the callback
                 }
+            }
         }
     }
 }
         }
     }
 }
@@ -4776,8 +5296,8 @@ mDNSexport void udns_validatelists(void *const v)
 
     DomainAuthInfo *info;
     for (info = m->AuthInfoList; info; info = info->next)
 
     DomainAuthInfo *info;
     for (info = m->AuthInfoList; info; info = info->next)
-        if (info->next == (DomainAuthInfo *)~0 || info->AutoTunnel == (const char*)~0)
-            LogMemCorruption("m->AuthInfoList: %p is garbage (%X)", info, info->AutoTunnel);
+        if (info->next == (DomainAuthInfo *)~0)
+            LogMemCorruption("m->AuthInfoList: %p is garbage", info);
 
     HostnameInfo *hi;
     for (hi = m->Hostnames; hi; hi = hi->next)
 
     HostnameInfo *hi;
     for (hi = m->Hostnames; hi; hi = hi->next)
@@ -4794,89 +5314,331 @@ mDNSexport void udns_validatelists(void *const v)
 // 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
 
 // 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)
+mDNSlocal void uDNS_DeleteWABQueries(mDNS *const m, SearchListElem *ptr, int delete)
+{
+    const char *name1 = mDNSNULL;
+    const char *name2 = mDNSNULL;
+    ARListElem **arList = &ptr->AuthRecs;
+    domainname namestorage1, namestorage2;
+    mStatus err;
+
+    // "delete" parameter indicates the type of query.
+    switch (delete)
+    {
+    case UDNS_WAB_BROWSE_QUERY:
+        mDNS_StopGetDomains(m, &ptr->BrowseQ);
+        mDNS_StopGetDomains(m, &ptr->DefBrowseQ);
+        name1 = mDNS_DomainTypeNames[mDNS_DomainTypeBrowse];
+        name2 = mDNS_DomainTypeNames[mDNS_DomainTypeBrowseDefault];
+        break;
+    case UDNS_WAB_LBROWSE_QUERY:
+        mDNS_StopGetDomains(m, &ptr->AutomaticBrowseQ);
+        name1 = mDNS_DomainTypeNames[mDNS_DomainTypeBrowseAutomatic];
+        break;
+    case UDNS_WAB_REG_QUERY:
+        mDNS_StopGetDomains(m, &ptr->RegisterQ);
+        mDNS_StopGetDomains(m, &ptr->DefRegisterQ);
+        name1 = mDNS_DomainTypeNames[mDNS_DomainTypeRegistration];
+        name2 = mDNS_DomainTypeNames[mDNS_DomainTypeRegistrationDefault];
+        break;
+    default:
+        LogMsg("uDNS_DeleteWABQueries: ERROR!! returning from default");
+        return;
+    }
+    // When we get the results to the domain enumeration queries, we add a LocalOnly
+    // entry. For example, if we issue a domain enumeration query for b._dns-sd._udp.xxxx.com,
+    // and when we get a response, we add a LocalOnly entry b._dns-sd._udp.local whose RDATA
+    // points to what we got in the response. Locate the appropriate LocalOnly entries and delete
+    // them.
+    if (name1)
+    {
+        MakeDomainNameFromDNSNameString(&namestorage1, name1);
+        AppendDNSNameString(&namestorage1, "local");
+    }
+    if (name2)
+    {
+        MakeDomainNameFromDNSNameString(&namestorage2, name2);
+        AppendDNSNameString(&namestorage2, "local");
+    }
+    while (*arList)
+    {
+        ARListElem *dereg = *arList;
+        if ((name1 && SameDomainName(&dereg->ar.namestorage, &namestorage1)) ||
+            (name2 && SameDomainName(&dereg->ar.namestorage, &namestorage2)))
+        {
+            LogInfo("uDNS_DeleteWABQueries: Deregistering PTR %##s -> %##s", dereg->ar.resrec.name->c, dereg->ar.resrec.rdata->u.name.c);
+            *arList = dereg->next;
+            err = mDNS_Deregister(m, &dereg->ar);
+            if (err) LogMsg("uDNS_DeleteWABQueries:: ERROR!! mDNS_Deregister returned %d", err);
+            // Memory will be freed in the FreeARElemCallback
+        }
+        else
+        {
+            LogInfo("uDNS_DeleteWABQueries: Skipping PTR %##s -> %##s", dereg->ar.resrec.name->c, dereg->ar.resrec.rdata->u.name.c);
+            arList = &(*arList)->next;
+        }
+    }
+}
+
+mDNSexport void uDNS_SetupWABQueries(mDNS *const m)
 {
     SearchListElem **p = &SearchList, *ptr;
     mStatus err;
 {
     SearchListElem **p = &SearchList, *ptr;
     mStatus err;
+    int action = 0;
 
     // step 1: mark each element for removal
 
     // step 1: mark each element for removal
-    for (ptr = SearchList; ptr; ptr = ptr->next) ptr->flag |= SLE_DELETE;
+    for (ptr = SearchList; ptr; ptr = ptr->next)
+        ptr->flag |= SLE_DELETE;
 
     // Make sure we have the search domains from the platform layer so that if we start the WAB
 
     // 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
+    // queries below, we have the latest information.
     mDNS_Lock(m);
     mDNS_Lock(m);
-    mDNSPlatformSetDNSConfig(m, mDNSfalse, mDNStrue, mDNSNULL, mDNSNULL, mDNSNULL);
+    if (!mDNSPlatformSetDNSConfig(m, mDNSfalse, mDNStrue, mDNSNULL, mDNSNULL, mDNSNULL, mDNSfalse))
+    {
+        // If the configuration did not change, clear the flag so that we don't free the searchlist.
+        // We still have to start the domain enumeration queries as we may not have started them
+        // before.
+        for (ptr = SearchList; ptr; ptr = ptr->next)
+            ptr->flag &= ~SLE_DELETE;
+        LogInfo("uDNS_SetupWABQueries: No config change");
+    }
     mDNS_Unlock(m);
 
     mDNS_Unlock(m);
 
-    if (action & UDNS_START_WAB_QUERY)
-        m->StartWABQueries = mDNStrue;
+    if (m->WABBrowseQueriesCount)
+        action |= UDNS_WAB_BROWSE_QUERY;
+    if (m->WABLBrowseQueriesCount)
+        action |= UDNS_WAB_LBROWSE_QUERY;
+    if (m->WABRegQueriesCount)
+        action |= UDNS_WAB_REG_QUERY;
+
 
     // delete elems marked for removal, do queries for elems marked add
     while (*p)
     {
         ptr = *p;
 
     // delete elems marked for removal, do queries for elems marked add
     while (*p)
     {
         ptr = *p;
-        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)
+        LogInfo("uDNS_SetupWABQueries:action 0x%x: Flags 0x%x,  AuthRecs %p, InterfaceID %p %##s", action, ptr->flag, ptr->AuthRecs, ptr->InterfaceID, ptr->domain.c);
+        // If SLE_DELETE is set, stop all the queries, deregister all the records and free the memory.
+        // Otherwise, check to see what the "action" requires. If a particular action bit is not set and
+        // we have started the corresponding queries as indicated by the "flags", stop those queries and
+        // deregister the records corresponding to them.
+        if ((ptr->flag & SLE_DELETE) ||
+            (!(action & UDNS_WAB_BROWSE_QUERY) && (ptr->flag & SLE_WAB_BROWSE_QUERY_STARTED)) ||
+            (!(action & UDNS_WAB_LBROWSE_QUERY) && (ptr->flag & SLE_WAB_LBROWSE_QUERY_STARTED)) ||
+            (!(action & UDNS_WAB_REG_QUERY) && (ptr->flag & SLE_WAB_REG_QUERY_STARTED)))
         {
         {
-            ARListElem *arList = ptr->AuthRecs;
-            ptr->AuthRecs = mDNSNULL;
-            *p = ptr->next;
+            if (ptr->flag & SLE_DELETE)
+            {
+                ARListElem *arList = ptr->AuthRecs;
+                ptr->AuthRecs = mDNSNULL;
+                *p = ptr->next;
+
+                // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries
+                // We suppressed the domain enumeration for scoped search domains below. When we enable that
+                // enable this.
+                if ((ptr->flag & SLE_WAB_BROWSE_QUERY_STARTED) &&
+                    !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any))
+                {
+                    LogInfo("uDNS_SetupWABQueries: DELETE  Browse for domain  %##s", ptr->domain.c);
+                    mDNS_StopGetDomains(m, &ptr->BrowseQ);
+                    mDNS_StopGetDomains(m, &ptr->DefBrowseQ);
+                }
+                if ((ptr->flag & SLE_WAB_LBROWSE_QUERY_STARTED) &&
+                    !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any))
+                {
+                    LogInfo("uDNS_SetupWABQueries: DELETE  Legacy Browse for domain  %##s", ptr->domain.c);
+                    mDNS_StopGetDomains(m, &ptr->AutomaticBrowseQ);
+                }
+                if ((ptr->flag & SLE_WAB_REG_QUERY_STARTED) &&
+                    !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any))
+                {
+                    LogInfo("uDNS_SetupWABQueries: DELETE  Registration for domain  %##s", ptr->domain.c);
+                    mDNS_StopGetDomains(m, &ptr->RegisterQ);
+                    mDNS_StopGetDomains(m, &ptr->DefRegisterQ);
+                }
+
+                mDNSPlatformMemFree(ptr);
+
+                // deregister records generated from answers to the query
+                while (arList)
+                {
+                    ARListElem *dereg = arList;
+                    arList = arList->next;
+                    LogInfo("uDNS_SetupWABQueries: DELETE Deregistering PTR %##s -> %##s", dereg->ar.resrec.name->c, dereg->ar.resrec.rdata->u.name.c);
+                    err = mDNS_Deregister(m, &dereg->ar);
+                    if (err) LogMsg("uDNS_SetupWABQueries:: ERROR!! mDNS_Deregister returned %d", err);
+                    // Memory will be freed in the FreeARElemCallback
+                }
+                continue;
+            }
 
             // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries
             // We suppressed the domain enumeration for scoped search domains below. When we enable that
             // enable this.
 
             // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries
             // We suppressed the domain enumeration for scoped search domains below. When we enable that
             // enable this.
-            if ((ptr->flag & SLE_WAB_QUERY_STARTED) &&
+            if (!(action & UDNS_WAB_BROWSE_QUERY) && (ptr->flag & SLE_WAB_BROWSE_QUERY_STARTED) &&
                 !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any))
             {
                 !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any))
             {
-                mDNS_StopGetDomains(m, &ptr->BrowseQ);
-                mDNS_StopGetDomains(m, &ptr->RegisterQ);
-                mDNS_StopGetDomains(m, &ptr->DefBrowseQ);
-                mDNS_StopGetDomains(m, &ptr->DefRegisterQ);
-                mDNS_StopGetDomains(m, &ptr->AutomaticBrowseQ);
+                LogInfo("uDNS_SetupWABQueries: Deleting Browse for domain  %##s", ptr->domain.c);
+                ptr->flag &= ~SLE_WAB_BROWSE_QUERY_STARTED;
+                uDNS_DeleteWABQueries(m, ptr, UDNS_WAB_BROWSE_QUERY);
             }
 
             }
 
-            mDNSPlatformMemFree(ptr);
+            if (!(action & UDNS_WAB_LBROWSE_QUERY) && (ptr->flag & SLE_WAB_LBROWSE_QUERY_STARTED) &&
+                !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any))
+            {
+                LogInfo("uDNS_SetupWABQueries: Deleting Legacy Browse for domain  %##s", ptr->domain.c);
+                ptr->flag &= ~SLE_WAB_LBROWSE_QUERY_STARTED;
+                uDNS_DeleteWABQueries(m, ptr, UDNS_WAB_LBROWSE_QUERY);
+            }
 
 
-            // deregister records generated from answers to the query
-            while (arList)
+            if (!(action & UDNS_WAB_REG_QUERY) && (ptr->flag & SLE_WAB_REG_QUERY_STARTED) &&
+                !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any))
             {
             {
-                ARListElem *dereg = arList;
-                arList = arList->next;
-                debugf("Deregistering PTR %##s -> %##s", dereg->ar.resrec.name->c, dereg->ar.resrec.rdata->u.name.c);
-                err = mDNS_Deregister(m, &dereg->ar);
-                if (err) LogMsg("uDNS_SetupSearchDomains:: ERROR!! mDNS_Deregister returned %d", err);
-                // Memory will be freed in the FreeARElemCallback
+                LogInfo("uDNS_SetupWABQueries: Deleting Registration for domain  %##s", ptr->domain.c);
+                ptr->flag &= ~SLE_WAB_REG_QUERY_STARTED;
+                uDNS_DeleteWABQueries(m, ptr, UDNS_WAB_REG_QUERY);
             }
             }
-            continue;
+
+            // Fall through to handle the ADDs
         }
 
         }
 
-        if ((action & UDNS_START_WAB_QUERY) && !(ptr->flag & SLE_WAB_QUERY_STARTED))
+        if ((action & UDNS_WAB_BROWSE_QUERY) && !(ptr->flag & SLE_WAB_BROWSE_QUERY_STARTED))
         {
             // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries.
             // Also, suppress the domain enumeration for scoped search domains for now until there is a need.
             if (!SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any))
             {
         {
             // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries.
             // Also, suppress the domain enumeration for scoped search domains for now until there is a need.
             if (!SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any))
             {
-                mStatus err1, err2, err3, err4, err5;
+                mStatus err1, err2;
                 err1 = mDNS_GetDomains(m, &ptr->BrowseQ,          mDNS_DomainTypeBrowse,              &ptr->domain, ptr->InterfaceID, FoundDomain, ptr);
                 err1 = mDNS_GetDomains(m, &ptr->BrowseQ,          mDNS_DomainTypeBrowse,              &ptr->domain, ptr->InterfaceID, FoundDomain, ptr);
+                if (err1)
+                {
+                    LogMsg("uDNS_SetupWABQueries: GetDomains for domain %##s returned error(s):\n"
+                           "%d (mDNS_DomainTypeBrowse)\n", ptr->domain.c, err1);
+                }
+                else
+                {
+                    LogInfo("uDNS_SetupWABQueries: Starting Browse for domain %##s", ptr->domain.c);
+                }
                 err2 = mDNS_GetDomains(m, &ptr->DefBrowseQ,       mDNS_DomainTypeBrowseDefault,       &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"
-                           "%d (mDNS_DomainTypeBrowseDefault)\n"
-                           "%d (mDNS_DomainTypeRegistration)\n"
-                           "%d (mDNS_DomainTypeRegistrationDefault)"
+                if (err2)
+                {
+                    LogMsg("uDNS_SetupWABQueries: GetDomains for domain %##s returned error(s):\n"
+                           "%d (mDNS_DomainTypeBrowseDefault)\n", ptr->domain.c, err2);
+                }
+                else
+                {
+                    LogInfo("uDNS_SetupWABQueries: Starting Default Browse for domain %##s", ptr->domain.c);
+                }
+                // For simplicity, we mark a single bit for denoting that both the browse queries have started.
+                // It is not clear as to why one would fail to start and the other would succeed in starting up.
+                // If that happens, we will try to stop both the queries and one of them won't be in the list and
+                // it is not a hard error.
+                if (!err1 || !err2)
+                {
+                    ptr->flag |= SLE_WAB_BROWSE_QUERY_STARTED;
+                }
+            }
+        }
+        if ((action & UDNS_WAB_LBROWSE_QUERY) && !(ptr->flag & SLE_WAB_LBROWSE_QUERY_STARTED))
+        {
+            // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries.
+            // Also, suppress the domain enumeration for scoped search domains for now until there is a need.
+            if (!SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any))
+            {
+                mStatus err1;
+                err1 = mDNS_GetDomains(m, &ptr->AutomaticBrowseQ, mDNS_DomainTypeBrowseAutomatic,     &ptr->domain, ptr->InterfaceID, FoundDomain, ptr);
+                if (err1)
+                {
+                    LogMsg("uDNS_SetupWABQueries: GetDomains for domain %##s returned error(s):\n"
                            "%d (mDNS_DomainTypeBrowseAutomatic)\n",
                            "%d (mDNS_DomainTypeBrowseAutomatic)\n",
-                           ptr->domain.c, err1, err2, err3, err4, err5);
-                ptr->flag |= SLE_WAB_QUERY_STARTED;
+                           ptr->domain.c, err1);
+                }
+                else
+                {
+                    ptr->flag |= SLE_WAB_LBROWSE_QUERY_STARTED;
+                    LogInfo("uDNS_SetupWABQueries: Starting Legacy Browse for domain %##s", ptr->domain.c);
+                }
+            }
+        }
+        if ((action & UDNS_WAB_REG_QUERY) && !(ptr->flag & SLE_WAB_REG_QUERY_STARTED))
+        {
+            // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries.
+            // Also, suppress the domain enumeration for scoped search domains for now until there is a need.
+            if (!SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any))
+            {
+                mStatus err1, err2;
+                err1 = mDNS_GetDomains(m, &ptr->RegisterQ,        mDNS_DomainTypeRegistration,        &ptr->domain, ptr->InterfaceID, FoundDomain, ptr);
+                if (err1)
+                {
+                    LogMsg("uDNS_SetupWABQueries: GetDomains for domain %##s returned error(s):\n"
+                           "%d (mDNS_DomainTypeRegistration)\n", ptr->domain.c, err1);
+                }
+                else
+                {
+                    LogInfo("uDNS_SetupWABQueries: Starting Registration for domain %##s", ptr->domain.c);
+                }
+                err2 = mDNS_GetDomains(m, &ptr->DefRegisterQ,     mDNS_DomainTypeRegistrationDefault, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr);
+                if (err2)
+                {
+                    LogMsg("uDNS_SetupWABQueries: GetDomains for domain %##s returned error(s):\n"
+                           "%d (mDNS_DomainTypeRegistrationDefault)", ptr->domain.c, err2);
+                }
+                else
+                {
+                    LogInfo("uDNS_SetupWABQueries: Starting Default Registration for domain %##s", ptr->domain.c);
+                }
+                if (!err1 || !err2)
+                {
+                    ptr->flag |= SLE_WAB_REG_QUERY_STARTED;
+                }
             }
         }
 
         p = &ptr->next;
     }
             }
         }
 
         p = &ptr->next;
     }
-    return mStatus_NoError;
+}
+
+// mDNS_StartWABQueries is called once per API invocation where normally
+// one of the bits is set.
+mDNSexport void uDNS_StartWABQueries(mDNS *const m, int queryType)
+{
+    if (queryType & UDNS_WAB_BROWSE_QUERY)
+    {
+        m->WABBrowseQueriesCount++;
+        LogInfo("uDNS_StartWABQueries: Browse query count %d", m->WABBrowseQueriesCount);
+    }
+    if (queryType & UDNS_WAB_LBROWSE_QUERY)
+    {
+        m->WABLBrowseQueriesCount++;
+        LogInfo("uDNS_StartWABQueries: Legacy Browse query count %d", m->WABLBrowseQueriesCount);
+    }
+    if (queryType & UDNS_WAB_REG_QUERY)
+    {
+        m->WABRegQueriesCount++;
+        LogInfo("uDNS_StartWABQueries: Reg query count %d", m->WABRegQueriesCount);
+    }
+    uDNS_SetupWABQueries(m);
+}
+
+// mDNS_StopWABQueries is called once per API invocation where normally
+// one of the bits is set.
+mDNSexport void uDNS_StopWABQueries(mDNS *const m, int queryType)
+{
+    if (queryType & UDNS_WAB_BROWSE_QUERY)
+    {
+        m->WABBrowseQueriesCount--;
+        LogInfo("uDNS_StopWABQueries: Browse query count %d", m->WABBrowseQueriesCount);
+    }
+    if (queryType & UDNS_WAB_LBROWSE_QUERY)
+    {
+        m->WABLBrowseQueriesCount--;
+        LogInfo("uDNS_StopWABQueries: Legacy Browse query count %d", m->WABLBrowseQueriesCount);
+    }
+    if (queryType & UDNS_WAB_REG_QUERY)
+    {
+        m->WABRegQueriesCount--;
+        LogInfo("uDNS_StopWABQueries: Reg query count %d", m->WABRegQueriesCount);
+    }
+    uDNS_SetupWABQueries(m);
 }
 
 mDNSexport domainname  *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal)
 }
 
 mDNSexport domainname  *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal)
@@ -4890,7 +5652,7 @@ mDNSexport domainname  *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID
     // Skip the  domains that we already looked at before. Guard against "p"
     // being NULL. When search domains change we may not set the SearchListIndex
     // of the question to zero immediately e.g., domain enumeration query calls
     // Skip the  domains that we already looked at before. Guard against "p"
     // being NULL. When search domains change we may not set the SearchListIndex
     // of the question to zero immediately e.g., domain enumeration query calls
-    // uDNS_SetupSearchDomain which reads in the new search domain but does not
+    // uDNS_SetupWABQueries which reads in the new search domain but does not
     // restart the questions immediately. Questions are restarted as part of
     // network change and hence temporarily SearchListIndex may be out of range.
 
     // restart the questions immediately. Questions are restarted as part of
     // network change and hence temporarily SearchListIndex may be out of range.
 
@@ -4957,12 +5719,29 @@ mDNSlocal void FlushAddressCacheRecords(mDNS *const m)
 // Retry questions which has seach domains appended
 mDNSexport void RetrySearchDomainQuestions(mDNS *const m)
 {
 // Retry questions which has seach domains appended
 mDNSexport void RetrySearchDomainQuestions(mDNS *const m)
 {
+    DNSQuestion *q;
+    mDNSBool found = mDNSfalse;
+
+    // Check to see if there are any questions which needs search domains to be applied.
+    // If there is none, search domains can't possibly affect them.
+    for (q = m->Questions; q; q = q->next)
+    {
+        if (q->AppendSearchDomains)
+        {
+            found = mDNStrue;
+            break;
+        }
+    }
+    if (!found)
+    {
+        LogInfo("RetrySearchDomainQuestions: Questions with AppendSearchDomain not found");
+        return;
+    }
+    LogInfo("RetrySearchDomainQuestions: Question with AppendSearchDomain found %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
     // 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
     // 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);
 }
 
     mDNSCoreRestartAddressQueries(m, mDNStrue, FlushAddressCacheRecords, mDNSNULL, mDNSNULL);
 }
 
@@ -4984,3 +5763,233 @@ struct CompileTimeAssertionChecks_uDNS
     char sizecheck_tcpInfo_t     [(sizeof(tcpInfo_t)      <=  9056) ? 1 : -1];
     char sizecheck_SearchListElem[(sizeof(SearchListElem) <=  5000) ? 1 : -1];
 };
     char sizecheck_tcpInfo_t     [(sizeof(tcpInfo_t)      <=  9056) ? 1 : -1];
     char sizecheck_SearchListElem[(sizeof(SearchListElem) <=  5000) ? 1 : -1];
 };
+
+#else // !UNICAST_DISABLED
+
+mDNSexport const domainname *GetServiceTarget(mDNS *m, AuthRecord *const rr)
+{
+       (void) m;
+       (void) rr;
+
+       return mDNSNULL;
+}
+
+mDNSexport DomainAuthInfo *GetAuthInfoForName_internal(mDNS *m, const domainname *const name)
+{
+       (void) m;
+       (void) name;
+
+       return mDNSNULL;
+}
+
+mDNSexport DomainAuthInfo *GetAuthInfoForQuestion(mDNS *m, const DNSQuestion *const q)
+{
+       (void) m;
+       (void) q;
+
+       return mDNSNULL;
+}
+
+mDNSexport void startLLQHandshake(mDNS *m, DNSQuestion *q)
+{
+       (void) m;
+       (void) q;
+}
+
+mDNSexport void DisposeTCPConn(struct tcpInfo_t *tcp)
+{
+       (void) tcp;
+}
+
+mDNSexport mStatus mDNS_StartNATOperation_internal(mDNS *m, NATTraversalInfo *traversal)
+{
+       (void) m;
+       (void) traversal;
+
+       return mStatus_UnsupportedErr;
+}
+
+mDNSexport mStatus mDNS_StopNATOperation_internal(mDNS *m, NATTraversalInfo *traversal)
+{
+       (void) m;
+       (void) traversal;
+
+       return mStatus_UnsupportedErr;
+}
+
+mDNSexport void sendLLQRefresh(mDNS *m, DNSQuestion *q)
+{
+       (void) m;
+       (void) q;
+}
+
+mDNSexport ZoneData *StartGetZoneData(mDNS *const m, const domainname *const name, const ZoneService target, ZoneDataCallback callback, void *ZoneDataContext)
+{
+       (void) m;
+       (void) name;
+       (void) target;
+       (void) callback;
+       (void) ZoneDataContext;
+
+       return mDNSNULL;
+}
+
+mDNSexport void RecordRegistrationGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneData)
+{
+       (void) m;
+       (void) err;
+       (void) zoneData;
+}
+
+mDNSexport uDNS_LLQType uDNS_recvLLQResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end,
+                                             const mDNSAddr *const srcaddr, const mDNSIPPort srcport, DNSQuestion **matchQuestion)
+{
+       (void) m;
+       (void) msg;
+       (void) end;
+       (void) srcaddr;
+       (void) srcport;
+       (void) matchQuestion;
+
+       return uDNS_LLQ_Not;
+}
+
+mDNSexport void PenalizeDNSServer(mDNS *const m, DNSQuestion *q, mDNSOpaque16 responseFlags)
+{
+       (void) m;
+       (void) q;
+       (void) responseFlags;
+}
+
+mDNSexport void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID)
+{
+    (void) domain;
+    (void) InterfaceID;
+}
+
+mDNSexport void RetrySearchDomainQuestions(mDNS *const m)
+{
+    (void) m;
+}
+
+mDNSexport mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info, const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, mDNSBool autoTunnel)
+{
+    (void) m;
+    (void) info;
+    (void) domain;
+    (void) keyname;
+    (void) b64keydata;
+    (void) hostname;
+    (void) port;
+    (void) autoTunnel;
+    
+    return mStatus_UnsupportedErr;
+}
+
+mDNSexport domainname  *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal)
+{
+    (void) m;
+    (void) InterfaceID;
+    (void) searchIndex;
+    (void) ignoreDotLocal;
+    
+    return mDNSNULL;
+}
+
+mDNSexport DomainAuthInfo *GetAuthInfoForName(mDNS *m, const domainname *const name)
+{
+    (void) m;
+    (void) name;
+    
+    return mDNSNULL;
+}
+
+mDNSexport mStatus mDNS_StartNATOperation(mDNS *const m, NATTraversalInfo *traversal)
+{
+    (void) m;
+    (void) traversal;
+    
+    return mStatus_UnsupportedErr;
+}
+
+mDNSexport mStatus mDNS_StopNATOperation(mDNS *const m, NATTraversalInfo *traversal)
+{
+    (void) m;
+    (void) traversal;
+    
+    return mStatus_UnsupportedErr;
+}
+
+mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSs32 serviceID, const mDNSAddr *addr,
+                                        const mDNSIPPort port, mDNSu32 scoped, mDNSu32 timeout, mDNSBool cellIntf, mDNSu16 resGroupID, mDNSBool reqA,
+                                        mDNSBool reqAAAA, mDNSBool reqDO)
+{
+    (void) m;
+    (void) d;
+    (void) interface;
+    (void) serviceID;
+    (void) addr;
+    (void) port;
+    (void) scoped;
+    (void) timeout;
+    (void) cellIntf;
+    (void) resGroupID;
+    (void) reqA;
+    (void) reqAAAA;
+    (void) reqDO;
+    
+    return mDNSNULL;
+}
+
+mDNSexport void uDNS_SetupWABQueries(mDNS *const m)
+{
+    (void) m;
+}
+
+mDNSexport void uDNS_StartWABQueries(mDNS *const m, int queryType)
+{
+    (void) m;
+    (void) queryType;
+}
+
+mDNSexport void uDNS_StopWABQueries(mDNS *const m, int queryType)
+{
+    (void) m;
+    (void) queryType;
+}
+
+mDNSexport void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext)
+{
+    (void) m;
+    (void) fqdn;
+    (void) StatusCallback;
+    (void) StatusContext;
+}
+mDNSexport void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, const mDNSAddr *v6addr, const mDNSAddr *router)
+{
+    (void) m;
+    (void) v4addr;
+    (void) v6addr;
+    (void) router;
+}
+
+mDNSexport void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn)
+{
+    (void) m;
+    (void) fqdn;
+}
+
+mDNSexport void RecreateNATMappings(mDNS *const m, const mDNSu32 waitTicks)
+{
+    (void) m;
+    (void) waitTicks;
+}
+
+mDNSexport mDNSBool IsGetZoneDataQuestion(DNSQuestion *q)
+{
+    (void)q;
+    
+    return mDNSfalse;
+}
+
+#endif // !UNICAST_DISABLED
index 02bdd1c8eee25194807c86659846c66e97c717a4..eca8b70152f6855686bd9fe38975da6c00af28a8 100755 (executable)
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 4 -*-
  *
 /* -*- Mode: C; tab-width: 4 -*-
  *
- * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2002-2013 Apple Computer, 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.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -32,8 +32,9 @@ extern "C" {
 //#define MAX_UCAST_POLL_INTERVAL (1 * 60 * mDNSPlatformOneSecond)
 #define LLQ_POLL_INTERVAL       (15 * 60 * mDNSPlatformOneSecond) // Polling interval for zones w/ an advertised LLQ port (ie not static zones) if LLQ fails due to NAT, etc.
 #define RESPONSE_WINDOW (60 * mDNSPlatformOneSecond)         // require server responses within one minute of request
 //#define MAX_UCAST_POLL_INTERVAL (1 * 60 * mDNSPlatformOneSecond)
 #define LLQ_POLL_INTERVAL       (15 * 60 * mDNSPlatformOneSecond) // Polling interval for zones w/ an advertised LLQ port (ie not static zones) if LLQ fails due to NAT, etc.
 #define RESPONSE_WINDOW (60 * mDNSPlatformOneSecond)         // require server responses within one minute of request
-#define MAX_UCAST_UNANSWERED_QUERIES 2                       // the number of unanswered queries from any one uDNS server before trying another server
-#define DNSSERVER_PENALTY_TIME (60 * mDNSPlatformOneSecond) // number of seconds for which new questions don't pick this server
+#define MAX_DNSSEC_UNANSWERED_QUERIES 1                      // number of unanswered queries from any one uDNS server before turning off DNSSEC Validation
+#define MAX_UCAST_UNANSWERED_QUERIES 2                       // number of unanswered queries from any one uDNS server before trying another server
+#define DNSSERVER_PENALTY_TIME (60 * mDNSPlatformOneSecond)  // number of seconds for which new questions don't pick this server
 
 // On some interfaces, we want to delay the first retransmission to a minimum of 2 seconds
 // rather than the default (1 second).
 
 // On some interfaces, we want to delay the first retransmission to a minimum of 2 seconds
 // rather than the default (1 second).
@@ -45,6 +46,10 @@ extern "C" {
 #define QuestionIntervalStep2 (QuestionIntervalStep*QuestionIntervalStep)
 #define QuestionIntervalStep3 (QuestionIntervalStep*QuestionIntervalStep*QuestionIntervalStep)
 #define InitialQuestionInterval ((mDNSPlatformOneSecond + QuestionIntervalStep-1) / QuestionIntervalStep)
 #define QuestionIntervalStep2 (QuestionIntervalStep*QuestionIntervalStep)
 #define QuestionIntervalStep3 (QuestionIntervalStep*QuestionIntervalStep*QuestionIntervalStep)
 #define InitialQuestionInterval ((mDNSPlatformOneSecond + QuestionIntervalStep-1) / QuestionIntervalStep)
+#define MaxQuestionInterval         (3600 * mDNSPlatformOneSecond)
+
+// just move to MaxQuestionInterval once over this threshold
+#define QuestionIntervalThreshold   (QuestionIntervalStep3 * mDNSPlatformOneSecond)
 
 // For Unicast record registrations, we initialize the interval to 1 second. When we send any query for
 // the record registration e.g., GetZoneData, we always back off by QuestionIntervalStep
 
 // For Unicast record registrations, we initialize the interval to 1 second. When we send any query for
 // the record registration e.g., GetZoneData, we always back off by QuestionIntervalStep
@@ -66,6 +71,11 @@ extern "C" {
 // which accomodates two DNS servers and two queries per DNS server.
 #define DEFAULT_UDNSSEC_TIMEOUT    10 // in seconds
 
 // which accomodates two DNS servers and two queries per DNS server.
 #define DEFAULT_UDNSSEC_TIMEOUT    10 // in seconds
 
+// If we are sending queries with EDNS0/DO option and we have no indications that the server
+// is DNSSEC aware and we have already reached MAX_DNSSEC_RETRANSMISSIONS, we disable
+// validation (for optional case only) for any questions that uses this server
+#define MAX_DNSSEC_RETRANSMISSIONS 3
+
 // Entry points into unicast-specific routines
 
 extern void LLQGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo);
 // Entry points into unicast-specific routines
 
 extern void LLQGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo);
@@ -104,14 +114,16 @@ extern void CheckNATMappings(mDNS *m);
 
 extern mStatus         uDNS_SetupDNSConfig(mDNS *const m);
 
 
 extern mStatus         uDNS_SetupDNSConfig(mDNS *const m);
 
-// uDNS_SetupSearchDomains by default adds search domains. It also can be called with one or
-// more values for "action" which does the following:
-//
-// -UDNS_START_WAB_QUERY - start Wide Area Bonjour (domain enumeration) queries
+// uDNS_SetupWABQueries reads search domains from the platform layer and starts the Wide Area Bonjour
+// (WAB) domain enumeration queries if necessary.
 
 
-#define UDNS_START_WAB_QUERY    0x00000001
+#define UDNS_WAB_BROWSE_QUERY    0x00000001 // Browse queries (b, db)
+#define UDNS_WAB_LBROWSE_QUERY   0x00000002 // Browse queries (lb)
+#define UDNS_WAB_REG_QUERY       0x00000004 // Registration queries (r and dr)
 
 
-extern mStatus         uDNS_SetupSearchDomains(mDNS *const m, int action);
+extern void uDNS_SetupWABQueries(mDNS *const m);
+extern void uDNS_StartWABQueries(mDNS *const m, int queryType);
+extern void uDNS_StopWABQueries(mDNS *const m, int queryType);
 extern domainname      *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal);
 
 typedef enum
 extern domainname      *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal);
 
 typedef enum
@@ -128,9 +140,9 @@ extern DomainAuthInfo *GetAuthInfoForQuestion(mDNS *m, const DNSQuestion *const
 extern void DisposeTCPConn(struct tcpInfo_t *tcp);
 
 // NAT traversal
 extern void DisposeTCPConn(struct tcpInfo_t *tcp);
 
 // NAT traversal
-extern void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *pkt, mDNSu16 len); // Called for each received NAT-PMP packet
+extern void uDNS_ReceiveNATPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *pkt, mDNSu16 len); // Called for each received PCP or NAT-PMP packet
 extern void natTraversalHandleAddressReply(mDNS *const m, mDNSu16 err, mDNSv4Addr ExtAddr);
 extern void natTraversalHandleAddressReply(mDNS *const m, mDNSu16 err, mDNSv4Addr ExtAddr);
-extern void natTraversalHandlePortMapReply(mDNS *const m, NATTraversalInfo *n, const mDNSInterfaceID InterfaceID, mDNSu16 err, mDNSIPPort extport, mDNSu32 lease);
+extern void natTraversalHandlePortMapReply(mDNS *const m, NATTraversalInfo *n, const mDNSInterfaceID InterfaceID, mDNSu16 err, mDNSIPPort extport, mDNSu32 lease, NATTProtocol protocol);
 
 #ifdef  __cplusplus
 }
 
 #ifdef  __cplusplus
 }
index f705a090064491b58b16e1eb66372a51afda6214..b930818939fcc35fd27d77cbf95ed55b0224fd2c 100644 (file)
@@ -17,7 +17,7 @@
 
 #include <CoreFoundation/CoreFoundation.h>
 #include <CoreFoundation/CFXPCBridge.h>
 
 #include <CoreFoundation/CoreFoundation.h>
 #include <CoreFoundation/CFXPCBridge.h>
-#include <dns_sd.h>
+#include "dns_sd.h"
 #include <UserEventAgentInterface.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <UserEventAgentInterface.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -855,8 +855,11 @@ NetBrowserInfo* NetBrowserInfoCreate(CFStringRef serviceType, CFStringRef domain
     if (domain)
     {
         CFIndex domainSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(domain), kCFStringEncodingUTF8);
     if (domain)
     {
         CFIndex domainSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(domain), kCFStringEncodingUTF8);
-        cDomain = calloc(serviceSize, 1);
-        success = success && CFStringGetCString(domain, cDomain, domainSize, kCFStringEncodingUTF8);
+        if (domainSize)
+        {
+            cDomain = calloc(domainSize, 1);
+            success = success && CFStringGetCString(domain, cDomain, domainSize, kCFStringEncodingUTF8);
+        }
     }
 
     if (!success)
     }
 
     if (!success)
diff --git a/mDNSMacOSX/CUPolicy.c b/mDNSMacOSX/CUPolicy.c
new file mode 100644 (file)
index 0000000..434e65c
--- /dev/null
@@ -0,0 +1,91 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2013 Apple Computer, 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 "mDNSMacOSX.h"
+#include <network/config.h>
+
+#if TARGET_OS_IPHONE
+
+mDNSexport void CUPInit(mDNS *const m)
+{
+    
+    m->p->handle = cellular_usage_policy_create_client();
+    if (!m->p->handle)
+    {
+        LogMsg("CUPInit: cellular_usage_policy_create_client failed");
+    }
+}
+
+mDNSexport mDNSBool mDNSPlatformAllowPID(mDNS *const m, DNSQuestion *q)
+{
+    // Currently the policy applies only for DNS requests sent over cellular interface
+    if (m->p->handle && q->qDNSServer && q->qDNSServer->cellIntf)
+    {
+        mDNSBool allowed;
+        if (q->pid)
+        {
+            allowed = (mDNSBool) cellular_usage_policy_is_data_allowed_for_pid(m->p->handle, q->pid);
+            if (!allowed)
+            {
+                xpc_object_t pidx = xpc_uint64_create(q->pid);
+                if (pidx)
+                {
+                    network_config_cellular_blocked_notify(pidx, NULL, NULL);
+                    LogInfo("mDNSPlaformAllowPID: Notified PID(%d) for %##s (%s)", q->pid, q->qname.c, DNSTypeName(q->qtype));
+                    xpc_release(pidx);
+                }
+            }
+        }
+        else
+        {
+            allowed = (mDNSBool) cellular_usage_policy_is_data_allowed_for_uuid(m->p->handle, q->uuid);
+            if (!allowed)
+            {
+                xpc_object_t uuidx = xpc_uuid_create(q->uuid);
+                if (uuidx)
+                {
+                    network_config_cellular_blocked_notify(NULL, uuidx, NULL);
+                    LogInfo("mDNSPlaformAllowPID: Notified UUID for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+                    xpc_release(uuidx);
+                }
+            }
+        }
+        return allowed;
+    }
+    else
+    {
+        return mDNStrue;
+    }
+}
+
+#else // TARGET_OS_IPHONE
+
+mDNSexport void CUPInit(mDNS *const m)
+{
+    (void)m; //unused
+}
+
+mDNSexport mDNSBool mDNSPlatformAllowPID(mDNS *const m, DNSQuestion *q)
+{
+    (void)m;    //unused
+    (void)q;    //unused
+    //LogMsg("mDNSPlatformAllowPID: %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+    return mDNStrue;
+}
+
+#endif // TARGET_OS_IPHONE
+
index 4223ca21c61b4a96be709228907395b17fc1c13d..408b3a2246d378dc937f71fa3c4c2411062d0501 100644 (file)
 #include "CryptoAlg.h"
 #include "CryptoSupport.h"
 #include "dnssec.h"
 #include "CryptoAlg.h"
 #include "CryptoSupport.h"
 #include "dnssec.h"
+#include "DNSSECSupport.h"
 
 #if TARGET_OS_IPHONE
 #include "SecRSAKey.h"                  // For RSA_SHA1 etc. verification
 
 #if TARGET_OS_IPHONE
 #include "SecRSAKey.h"                  // For RSA_SHA1 etc. verification
+#else
+#include <Security/Security.h>
+#endif
+
+#if !TARGET_OS_IPHONE
+mDNSlocal SecKeyRef SecKeyCreateRSAPublicKey_OSX(unsigned char *asn1, int length);
 #endif
 
 typedef struct
 {
 #endif
 
 typedef struct
 {
-#if DISPATCH_API_VERSION >= 20111008
     dispatch_data_t encData;
     dispatch_data_t encMap;
     dispatch_data_t encNULL;
     dispatch_data_t encData;
     dispatch_data_t encMap;
     dispatch_data_t encNULL;
-#endif
 }encContext;
 
 mDNSlocal mStatus enc_create(AlgContext *ctx)
 }encContext;
 
 mDNSlocal mStatus enc_create(AlgContext *ctx)
@@ -56,7 +61,6 @@ mDNSlocal mStatus enc_create(AlgContext *ctx)
         LogMsg("enc_create: Unsupported algorithm %d", ctx->alg);
         return mStatus_BadParamErr;
     }
         LogMsg("enc_create: Unsupported algorithm %d", ctx->alg);
         return mStatus_BadParamErr;
     }
-#if DISPATCH_API_VERSION >= 20111008
     ptr->encData = NULL;
     ptr->encMap = NULL;
     // The encoded data is not NULL terminated. So, we concatenate a null byte later when we encode and map
     ptr->encData = NULL;
     ptr->encMap = NULL;
     // The encoded data is not NULL terminated. So, we concatenate a null byte later when we encode and map
@@ -67,7 +71,6 @@ mDNSlocal mStatus enc_create(AlgContext *ctx)
         mDNSPlatformMemFree(ptr);
         return mStatus_NoMemoryErr;
     }
         mDNSPlatformMemFree(ptr);
         return mStatus_NoMemoryErr;
     }
-#endif
     ctx->context = ptr;
     return mStatus_NoError;
 }
     ctx->context = ptr;
     return mStatus_NoError;
 }
@@ -75,23 +78,20 @@ mDNSlocal mStatus enc_create(AlgContext *ctx)
 mDNSlocal mStatus enc_destroy(AlgContext *ctx)
 {
     encContext *ptr = (encContext *)ctx->context;
 mDNSlocal mStatus enc_destroy(AlgContext *ctx)
 {
     encContext *ptr = (encContext *)ctx->context;
-#if DISPATCH_API_VERSION >= 20111008
     if (ptr->encData) dispatch_release(ptr->encData);
     if (ptr->encMap) dispatch_release(ptr->encMap);
     if (ptr->encNULL) dispatch_release(ptr->encNULL);
     if (ptr->encData) dispatch_release(ptr->encData);
     if (ptr->encMap) dispatch_release(ptr->encMap);
     if (ptr->encNULL) dispatch_release(ptr->encNULL);
-#endif
     mDNSPlatformMemFree(ptr);
     return mStatus_NoError;
 }
 
     mDNSPlatformMemFree(ptr);
     return mStatus_NoError;
 }
 
-mDNSlocal mStatus enc_add(AlgContext *ctx, void *data, mDNSu32 len)
+mDNSlocal mStatus enc_add(AlgContext *ctx, const void *data, mDNSu32 len)
 {
     switch (ctx->alg)
     {
     case ENC_BASE32:
     case ENC_BASE64:
     {
 {
     switch (ctx->alg)
     {
     case ENC_BASE32:
     case ENC_BASE64:
     {
-#if DISPATCH_API_VERSION >= 20111008
         encContext *ptr = (encContext *)ctx->context;
         dispatch_data_t src_data = dispatch_data_create(data, len, dispatch_get_global_queue(0, 0), ^{});
         if (!src_data)
         encContext *ptr = (encContext *)ctx->context;
         dispatch_data_t src_data = dispatch_data_create(data, len, dispatch_get_global_queue(0, 0), ^{});
         if (!src_data)
@@ -100,7 +100,7 @@ mDNSlocal mStatus enc_add(AlgContext *ctx, void *data, mDNSu32 len)
             return mStatus_BadParamErr;
         }
         dispatch_data_t dest_data = dispatch_data_create_with_transform(src_data, DISPATCH_DATA_FORMAT_TYPE_NONE,
             return mStatus_BadParamErr;
         }
         dispatch_data_t dest_data = dispatch_data_create_with_transform(src_data, DISPATCH_DATA_FORMAT_TYPE_NONE,
-                                                                        (ctx->alg == ENC_BASE32 ? DISPATCH_DATA_FORMAT_TYPE_BASE32 : DISPATCH_DATA_FORMAT_TYPE_BASE64));
+                                                                        (ctx->alg == ENC_BASE32 ? DISPATCH_DATA_FORMAT_TYPE_BASE32HEX : DISPATCH_DATA_FORMAT_TYPE_BASE64));
         dispatch_release(src_data);
         if (!dest_data)
         {
         dispatch_release(src_data);
         if (!dest_data)
         {
@@ -108,10 +108,7 @@ mDNSlocal mStatus enc_add(AlgContext *ctx, void *data, mDNSu32 len)
             return mStatus_BadParamErr;
         }
         ptr->encData = dest_data;
             return mStatus_BadParamErr;
         }
         ptr->encData = dest_data;
-#else
-        (void)data;
-        (void)len;
-#endif
+
         return mStatus_NoError;
     }
     default:
         return mStatus_NoError;
     }
     default:
@@ -129,7 +126,6 @@ mDNSlocal mDNSu8* enc_encode(AlgContext *ctx)
     case ENC_BASE32:
     case ENC_BASE64:
     {
     case ENC_BASE32:
     case ENC_BASE64:
     {
-#if DISPATCH_API_VERSION >= 20111008
         encContext *ptr = (encContext *)ctx->context;
         size_t size;
         dispatch_data_t dest_data = ptr->encData;
         encContext *ptr = (encContext *)ctx->context;
         size_t size;
         dispatch_data_t dest_data = ptr->encData;
@@ -150,7 +146,7 @@ mDNSlocal mDNSu8* enc_encode(AlgContext *ctx)
         dispatch_release(dest_data);
         ptr->encData = data;
         ptr->encMap = map;
         dispatch_release(dest_data);
         ptr->encData = data;
         ptr->encMap = map;
-#endif
+
         return (mDNSu8 *)result;
     }
     default:
         return (mDNSu8 *)result;
     }
     default:
@@ -202,7 +198,7 @@ mDNSlocal mDNSu32 sha_len(AlgContext *ctx)
     }
 }
 
     }
 }
 
-mDNSlocal mStatus sha_add(AlgContext *ctx, void *data, mDNSu32 len)
+mDNSlocal mStatus sha_add(AlgContext *ctx, const void *data, mDNSu32 len)
 {
     switch (ctx->alg)
     {
 {
     switch (ctx->alg)
     {
@@ -251,6 +247,34 @@ mDNSlocal mStatus sha_verify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu
         return mStatus_NoAuth;
 }
 
         return mStatus_NoAuth;
 }
 
+mDNSlocal mStatus sha_final(AlgContext *ctx, void *digestOut, mDNSu32 dlen)
+{
+    mDNSu8 digest[CC_SHA512_DIGEST_LENGTH];
+    mDNSu32 digestLen;
+
+    switch (ctx->alg)
+    {
+    case SHA1_DIGEST_TYPE:
+        digestLen = CC_SHA1_DIGEST_LENGTH;
+        CC_SHA1_Final(digest, (CC_SHA1_CTX *)ctx->context);
+        break;
+    case SHA256_DIGEST_TYPE:
+        digestLen = CC_SHA256_DIGEST_LENGTH;
+        CC_SHA256_Final(digest, (CC_SHA256_CTX *)ctx->context);
+        break;
+    default:
+        LogMsg("sha_final: Unsupported algorithm %d", ctx->alg);
+        return mStatus_BadParamErr;
+    }
+    if (dlen != digestLen)
+    {
+        LogMsg("sha_final(Alg %d): digest len mismatch len %u, expected %u", ctx->alg, (unsigned int)dlen, (unsigned int)digestLen);
+        return mStatus_BadParamErr;
+    }
+    memcpy(digestOut, digest, digestLen);
+    return mStatus_NoError;
+}
+
 mDNSlocal mStatus rsa_sha_create(AlgContext *ctx)
 {
     mDNSu8 *ptr;
 mDNSlocal mStatus rsa_sha_create(AlgContext *ctx)
 {
     mDNSu8 *ptr;
@@ -303,7 +327,7 @@ mDNSlocal mDNSu32 rsa_sha_len(AlgContext *ctx)
     }
 }
 
     }
 }
 
-mDNSlocal mStatus rsa_sha_add(AlgContext *ctx, void *data, mDNSu32 len)
+mDNSlocal mStatus rsa_sha_add(AlgContext *ctx, const void *data, mDNSu32 len)
 {
     switch (ctx->alg)
     {
 {
     switch (ctx->alg)
     {
@@ -324,7 +348,6 @@ mDNSlocal mStatus rsa_sha_add(AlgContext *ctx, void *data, mDNSu32 len)
     return mStatus_NoError;
 }
 
     return mStatus_NoError;
 }
 
-#if TARGET_OS_IPHONE
 mDNSlocal SecKeyRef rfc3110_import(const mDNSu8 *data, const mDNSu32 len)
 {
     static const int max_key_bytes = 4096 / 8;                // max DNSSEC supported modulus is 4096 bits
 mDNSlocal SecKeyRef rfc3110_import(const mDNSu8 *data, const mDNSu32 len)
 {
     static const int max_key_bytes = 4096 / 8;                // max DNSSEC supported modulus is 4096 bits
@@ -427,29 +450,38 @@ mDNSlocal SecKeyRef rfc3110_import(const mDNSu8 *data, const mDNSu32 len)
     for (i = 1; i <= exp_length; i++)
         asn1[index++] = data[i];
 
     for (i = 1; i <= exp_length; i++)
         asn1[index++] = data[i];
 
+#if TARGET_OS_IPHONE
     // index contains bytes written, use it for length
     // index contains bytes written, use it for length
-    return SecKeyCreateRSAPublicKey(NULL, asn1, index, kSecKeyEncodingPkcs1);
+    return (SecKeyCreateRSAPublicKey(NULL, asn1, index, kSecKeyEncodingPkcs1));
+#else
+    return (SecKeyCreateRSAPublicKey_OSX(asn1, index));
+#endif
 }
 
 }
 
+#if TARGET_OS_IPHONE
 mDNSlocal mStatus rsa_sha_verify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen)
 {
     SecKeyRef keyref;
     OSStatus result;
     mDNSu8 digest[CC_SHA512_DIGEST_LENGTH];
     int digestlen;
 mDNSlocal mStatus rsa_sha_verify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen)
 {
     SecKeyRef keyref;
     OSStatus result;
     mDNSu8 digest[CC_SHA512_DIGEST_LENGTH];
     int digestlen;
+    int cryptoAlg;
 
     switch (ctx->alg)
     {
     case CRYPTO_RSA_NSEC3_SHA1:
     case CRYPTO_RSA_SHA1:
 
     switch (ctx->alg)
     {
     case CRYPTO_RSA_NSEC3_SHA1:
     case CRYPTO_RSA_SHA1:
+        cryptoAlg = kSecPaddingPKCS1SHA1;
         digestlen = CC_SHA1_DIGEST_LENGTH;
         CC_SHA1_Final(digest, (CC_SHA1_CTX *)ctx->context);
         break;
     case CRYPTO_RSA_SHA256:
         digestlen = CC_SHA1_DIGEST_LENGTH;
         CC_SHA1_Final(digest, (CC_SHA1_CTX *)ctx->context);
         break;
     case CRYPTO_RSA_SHA256:
+        cryptoAlg = kSecPaddingPKCS1SHA256;
         digestlen = CC_SHA256_DIGEST_LENGTH;
         CC_SHA256_Final(digest, (CC_SHA256_CTX *)ctx->context);
         break;
     case CRYPTO_RSA_SHA512:
         digestlen = CC_SHA256_DIGEST_LENGTH;
         CC_SHA256_Final(digest, (CC_SHA256_CTX *)ctx->context);
         break;
     case CRYPTO_RSA_SHA512:
+        cryptoAlg = kSecPaddingPKCS1SHA512;
         digestlen = CC_SHA512_DIGEST_LENGTH;
         CC_SHA512_Final(digest, (CC_SHA512_CTX *)ctx->context);
         break;
         digestlen = CC_SHA512_DIGEST_LENGTH;
         CC_SHA512_Final(digest, (CC_SHA512_CTX *)ctx->context);
         break;
@@ -459,34 +491,204 @@ mDNSlocal mStatus rsa_sha_verify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, m
     }
 
     keyref = rfc3110_import(key, keylen);
     }
 
     keyref = rfc3110_import(key, keylen);
-    if (!key)
+    if (!keyref)
     {
         LogMsg("rsa_sha_verify: Error decoding rfc3110 key data");
         return mStatus_NoMemoryErr;
     }
     {
         LogMsg("rsa_sha_verify: Error decoding rfc3110 key data");
         return mStatus_NoMemoryErr;
     }
-    // TBD: Use the right algorithm when the support becomes available
-    result = SecKeyRawVerify(keyref, kSecPaddingPKCS1SHA1, digest, digestlen, signature, siglen);
-    LogMsg("rsa_sha_verify: result: %s (%ld)", result == noErr ? "PASS" : "FAIL", result);
-    if (result == noErr)
-        return mStatus_NoError;
-    else
+    result = SecKeyRawVerify(keyref, cryptoAlg, digest, digestlen, signature, siglen);
+    CFRelease(keyref);
+    if (result != noErr)
+    {
+        LogMsg("rsa_sha_verify: Failed for alg %d", ctx->alg);
         return mStatus_BadParamErr;
         return mStatus_BadParamErr;
+    }
+    else
+    {
+        LogInfo("rsa_sha_verify: Passed for alg %d", ctx->alg);
+        return mStatus_NoError;
+    }
 }
 }
-#else
+#else // TARGET_OS_IPHONE
+
+mDNSlocal SecKeyRef SecKeyCreateRSAPublicKey_OSX(unsigned char *asn1, int length)
+{
+    SecKeyRef result = NULL;
+    
+    SecExternalFormat extFormat = kSecFormatBSAFE;
+    SecExternalItemType itemType = kSecItemTypePublicKey;
+    CFArrayRef outArray = NULL;
+
+    CFDataRef keyData = CFDataCreate(NULL, asn1, length);
+    if (!keyData)
+        return NULL;
+
+    OSStatus err =  SecItemImport(keyData, NULL, &extFormat, &itemType, 0, NULL, NULL, &outArray);
+    
+    CFRelease(keyData);
+    if (noErr != err || outArray == NULL)
+    {
+        if (outArray)
+            CFRelease(outArray);
+        return NULL;
+    }
+    
+    result = (SecKeyRef)CFArrayGetValueAtIndex(outArray, 0);
+    if (result == NULL)
+    {
+        CFRelease(outArray);
+        return NULL;
+    }
+    
+    CFRetain(result);
+    CFRelease(outArray);
+    return result;
+}
+
+mDNSlocal Boolean VerifyData(SecKeyRef key, CFStringRef digestStr, mDNSu8 *digest, int dlen, int digestlenAttr, mDNSu8 *sig, int siglen, CFStringRef digest_type)
+{
+    CFErrorRef error;
+    Boolean ret;
+    
+    CFDataRef signature = CFDataCreate(NULL, sig, siglen);
+    if (!signature)
+        return false;
+    
+    SecTransformRef verifyXForm = SecVerifyTransformCreate(key, signature, &error);
+    CFRelease(signature);
+    if (verifyXForm == NULL)
+    {
+        return false;
+    }
+    
+    // tell the transform what type of data it is geting
+    if (!SecTransformSetAttribute(verifyXForm, kSecInputIsAttributeName, digest_type, &error))
+    {
+        LogMsg("VerifyData: SecTransformSetAttribute digest_type");
+        goto err;
+    }
+    
+    if (!SecTransformSetAttribute(verifyXForm, kSecDigestTypeAttribute, digestStr, &error))
+    {
+        LogMsg("VerifyData: SecTransformSetAttribute digestStr");
+        goto err;
+    }
+    
+    CFNumberRef digestLengthRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &digestlenAttr);
+    if (digestLengthRef == NULL)
+    {
+        LogMsg("VerifyData: CFNumberCreate failed");
+        goto err;
+    }
+    
+    ret = SecTransformSetAttribute(verifyXForm, kSecDigestLengthAttribute, digestLengthRef, &error);
+    CFRelease(digestLengthRef);
+    if (!ret)
+    {
+        LogMsg("VerifyData: SecTransformSetAttribute digestLengthRef");
+        goto err;
+    }
+    
+    CFDataRef dataToSign = CFDataCreate(NULL, digest, dlen);
+    if (dataToSign ==  NULL)
+    {
+        LogMsg("VerifyData: CFDataCreate failed");
+        goto err;
+    }
+
+    ret = SecTransformSetAttribute(verifyXForm, kSecTransformInputAttributeName, dataToSign, &error);
+    CFRelease(dataToSign);
+    if (!ret)
+    {
+        LogMsg("VerifyData: SecTransformSetAttribute TransformAttributeName");
+        goto err;
+    }
+    
+    CFBooleanRef boolRef = SecTransformExecute(verifyXForm, &error);
+    CFRelease(verifyXForm);
+
+    if (error != NULL)
+    {
+        CFStringRef errStr = CFErrorCopyDescription(error);
+        char errorbuf[128];
+        errorbuf[0] = 0;
+        if (errStr != NULL)
+        {
+            if (!CFStringGetCString(errStr, errorbuf, sizeof(errorbuf), kCFStringEncodingUTF8))
+            {
+                LogMsg("VerifyData: CFStringGetCString failed");
+            }
+        }
+        LogMsg("VerifyData: SecTransformExecute failed with %s", errorbuf);
+        return false;
+    }
+    return CFEqual(boolRef, kCFBooleanTrue);    
+err:
+    CFRelease(verifyXForm);
+    return false;
+}
+
 mDNSlocal mStatus rsa_sha_verify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen)
 {
 mDNSlocal mStatus rsa_sha_verify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen)
 {
-    (void)ctx;
-    (void)key;
-    (void)keylen;
-    (void)signature;
-    (void)siglen;
-    return mStatus_NoError;
+    SecKeyRef keyref;
+    mDNSu8 digest[CC_SHA512_DIGEST_LENGTH];
+    int digestlen;
+    int digestlenAttr;
+    CFStringRef digestStr;
+    mDNSBool ret;
+
+    switch (ctx->alg)
+    {
+    case CRYPTO_RSA_NSEC3_SHA1:
+    case CRYPTO_RSA_SHA1:
+        digestStr = kSecDigestSHA1;
+        digestlen = CC_SHA1_DIGEST_LENGTH;
+        digestlenAttr = 0;
+        CC_SHA1_Final(digest, (CC_SHA1_CTX *)ctx->context);
+        break;
+    case CRYPTO_RSA_SHA256:
+        digestStr = kSecDigestSHA2;
+        digestlen = CC_SHA256_DIGEST_LENGTH;
+        digestlenAttr = 256;
+        CC_SHA256_Final(digest, (CC_SHA256_CTX *)ctx->context);
+        break;
+    case CRYPTO_RSA_SHA512:
+        digestStr = kSecDigestSHA2;
+        digestlen = CC_SHA512_DIGEST_LENGTH;
+        digestlenAttr = 512;
+        CC_SHA512_Final(digest, (CC_SHA512_CTX *)ctx->context);
+        break;
+    default:
+        LogMsg("rsa_sha_verify: Unsupported algorithm %d", ctx->alg);
+        return mStatus_BadParamErr;
+    }
+
+    keyref = rfc3110_import(key, keylen);
+    if (!keyref)
+    {
+        LogMsg("rsa_sha_verify: Error decoding rfc3110 key data");
+        return mStatus_NoMemoryErr;
+    }
+    ret = VerifyData(keyref, digestStr, digest, digestlen, digestlenAttr, signature, siglen, kSecInputIsDigest);
+    CFRelease(keyref);
+    if (!ret)
+    {
+        LogMsg("rsa_sha_verify: Failed for alg %d", ctx->alg);
+        return mStatus_BadParamErr;
+    }
+    else
+    {
+        LogInfo("rsa_sha_verify: Passed for alg %d", ctx->alg);
+        return mStatus_NoError;
+    }
 }
 }
-#endif
+#endif // TARGET_OS_IPHONE
+
+AlgFuncs sha_funcs = {sha_create, sha_destroy, sha_len, sha_add, sha_verify, mDNSNULL, sha_final};
+AlgFuncs rsa_sha_funcs = {rsa_sha_create, rsa_sha_destroy, rsa_sha_len, rsa_sha_add, rsa_sha_verify, mDNSNULL, mDNSNULL};
+AlgFuncs enc_funcs = {enc_create, enc_destroy, mDNSNULL, enc_add, mDNSNULL, enc_encode, mDNSNULL};
 
 
-AlgFuncs sha_funcs = {sha_create, sha_destroy, sha_len, sha_add, sha_verify, mDNSNULL};
-AlgFuncs rsa_sha_funcs = {rsa_sha_create, rsa_sha_destroy, rsa_sha_len, rsa_sha_add, rsa_sha_verify, mDNSNULL};
-AlgFuncs enc_funcs = {enc_create, enc_destroy, mDNSNULL, enc_add, mDNSNULL, enc_encode};
+#ifndef DNSSEC_DISABLED
 
 mDNSexport mStatus DNSSECCryptoInit(mDNS *const m)
 {
 
 mDNSexport mStatus DNSSECCryptoInit(mDNS *const m)
 {
@@ -517,8 +719,20 @@ mDNSexport mStatus DNSSECCryptoInit(mDNS *const m)
     if (result != mStatus_NoError)
         return result;
 
     if (result != mStatus_NoError)
         return result;
 
-    m->TrustAnchors = mDNSNULL;
-    m->notifyToken  = 0;
+    result = DNSSECPlatformInit(m);
+
+    return result;
+}
+
+#else // !DNSSEC_DISABLED
+
+mDNSexport mStatus DNSSECCryptoInit(mDNS *const m)
+{
+    (void) m;
     
     return mStatus_NoError;
 }
     
     return mStatus_NoError;
 }
+
+#endif // !DNSSEC_DISABLED
+
+
diff --git a/mDNSMacOSX/DNSProxySupport.c b/mDNSMacOSX/DNSProxySupport.c
new file mode 100644 (file)
index 0000000..042bbc8
--- /dev/null
@@ -0,0 +1,543 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2011 Apple Computer, 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 "mDNSEmbeddedAPI.h"
+#include "mDNSMacOSX.h"
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/event.h>
+
+#define ValidSocket(s) ((s) >= 0)
+
+// Global to store the 4 DNS Proxy Listeners (UDPv4/6, TCPv4/6)
+static int dp_listener[4];
+
+#define NUM_PROXY_TCP_CONNS 100
+
+typedef struct
+{
+    TCPSocket   sock;
+    DNSMessage  *reply;
+    mDNSu16     replyLen;
+    mDNSu32     nread;
+} ProxyTCPInfo_t;
+
+// returns -1 for failures including the other end closing the socket
+// returns 0 if successful in reading data, but still not read the data fully
+// returns 1 if successful in reading all the data
+mDNSlocal int ProxyTCPRead(ProxyTCPInfo_t *tcpInfo)
+{
+    long n;
+    mDNSBool closed; 
+
+    if (tcpInfo->nread < 2)         // First read the two-byte length preceeding the DNS message
+    {
+        mDNSu8 *lenptr = (mDNSu8 *)&tcpInfo->replyLen;
+        n = mDNSPlatformReadTCP(&tcpInfo->sock, lenptr + tcpInfo->nread, 2 - tcpInfo->nread, &closed);
+        if (n < 0 || closed)
+        {
+            LogMsg("ProxyTCPRead: attempt to read message length failed");
+            return -1;
+        }
+
+        tcpInfo->nread += n;
+        if (tcpInfo->nread < 2)
+        {
+            LogMsg("ProxyTCPRead: nread %d, n %d", tcpInfo->nread, n);
+            return 0;
+        }
+
+        tcpInfo->replyLen = (mDNSu16)((mDNSu16)lenptr[0] << 8 | lenptr[1]);
+        if (tcpInfo->replyLen < sizeof(DNSMessageHeader))
+        {
+            LogMsg("ProxyTCPRead: Message length too short (%d bytes)", tcpInfo->replyLen);
+            return -1;
+        }
+
+        tcpInfo->reply = mallocL("ProxyTCPInfo", tcpInfo->replyLen);
+        if (!tcpInfo->reply)
+        {
+            LogMsg("ProxyTCPRead: Memory failure");
+            return -1;
+        }
+    }
+
+    n = mDNSPlatformReadTCP(&tcpInfo->sock, ((char *)tcpInfo->reply) + (tcpInfo->nread - 2), tcpInfo->replyLen - (tcpInfo->nread - 2), &closed);
+
+    if (n < 0 || closed)
+    {
+        LogMsg("ProxyTCPRead: read failure n %d, closed %d", n, closed);
+        return -1;
+    }
+    tcpInfo->nread += n;
+    if ((tcpInfo->nread - 2) != tcpInfo->replyLen)
+        return 0;
+    else 
+        return 1;
+}
+
+mDNSlocal void ProxyTCPSocketCallBack(int s1, short filter, void *context)
+{
+    int ret;
+    struct sockaddr_storage from;
+    struct sockaddr_storage to;
+    mDNSAddr senderAddr, destAddr;
+    mDNSIPPort senderPort;
+    ProxyTCPInfo_t *ti = (ProxyTCPInfo_t *)context;
+    TCPSocket *sock = &ti->sock;
+    KQSocketSet *kq = &sock->ss;
+
+    (void) filter;
+
+    ret = ProxyTCPRead(ti);
+    if (ret == -1)
+    {
+        mDNSPlatformDisposeProxyContext(ti);
+        return; 
+    }
+    else if (!ret)
+    {
+        debugf("ProxyTCPReceive: Not yet read completely Actual length %d, Read length %d", ti->replyLen, ti->nread);
+        return;
+    }
+    // We read all the data and hence not interested in read events anymore
+    KQueueSet(s1, EV_DELETE, EVFILT_READ, sock->kqEntry);
+
+    mDNSPlatformMemZero(&to, sizeof(to));
+    mDNSPlatformMemZero(&from, sizeof(from));
+    socklen_t len = sizeof(to);
+    ret = getsockname(s1, (struct sockaddr*) &to, &len);
+    if (ret < 0)
+    {
+        LogMsg("ProxyTCPReceive: getsockname(fd=%d) errno %d", s1, errno);
+        mDNSPlatformDisposeProxyContext(ti);
+        return;
+    }
+    ret = getpeername(s1, (struct sockaddr*) &from, &len);
+    if (ret < 0)
+    {
+        LogMsg("ProxyTCPReceive: getpeername(fd=%d) errno %d", s1, errno);
+        mDNSPlatformDisposeProxyContext(ti);
+        return;
+    }
+
+    if (from.ss_family == AF_INET)
+    {
+        struct sockaddr_in *s = (struct sockaddr_in*)&from;
+
+        senderAddr.type = mDNSAddrType_IPv4;
+        senderAddr.ip.v4.NotAnInteger = s->sin_addr.s_addr;
+        senderPort.NotAnInteger = s->sin_port;
+
+        s = (struct sockaddr_in *)&to;
+        destAddr.type = mDNSAddrType_IPv4;
+        destAddr.ip.v4.NotAnInteger = s->sin_addr.s_addr;
+
+        LogInfo("ProxyTCPReceive received IPv4 packet(len %d) from %#-15a to %#-15a on skt %d %s", ti->replyLen, &senderAddr, &destAddr, s1, NULL);
+    }
+    else if (from.ss_family == AF_INET6)
+    {
+        struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&from;
+        senderAddr.type = mDNSAddrType_IPv6;
+        senderAddr.ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr;
+        senderPort.NotAnInteger = sin6->sin6_port;
+
+        sin6 = (struct sockaddr_in6 *)&to;
+        destAddr.type = mDNSAddrType_IPv6;
+        destAddr.ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr;
+
+        LogInfo("ProxyTCPReceive received IPv6 packet(len %d) from %#-15a to %#-15a on skt %d %s", ti->replyLen, &senderAddr, &destAddr, s1, NULL);
+    }
+    else
+    {
+        LogMsg("ProxyTCPReceive from is unknown address family %d", from.ss_family);
+        mDNSPlatformDisposeProxyContext(ti);
+        return;
+    }
+
+    // We pass sock for the TCPSocket and the "ti" for context as that's what we want to free at the end.
+    // In the UDP case, there is just a single socket and nothing to free. Hence, the context (last argument)
+    // would be NULL.
+    kq->m->p->TCPProxyCallback(kq->m, sock, ti->reply, (mDNSu8 *)ti->reply + ti->replyLen, &senderAddr, senderPort, &destAddr,
+        UnicastDNSPort, 0, ti);
+}
+
+mDNSlocal void ProxyTCPAccept(int s1, short filter, void *context)
+{
+    int newfd;
+    struct sockaddr_storage ss;
+    socklen_t sslen = sizeof(ss);
+    const int on = 1;
+    KQSocketSet *listenSet = (KQSocketSet *)context;
+
+    (void) filter;
+
+    while ((newfd = accept(s1, (struct sockaddr *)&ss, &sslen)) != -1)
+    {
+        int err;
+        int *s;
+        KQueueEntry *k;
+        KQSocketSet *kq;
+
+        // Even though we just need a single KQueueEntry, for simplicity we re-use
+        // the KQSocketSet
+        ProxyTCPInfo_t *ti = mallocL("ProxyTCPContext", sizeof(ProxyTCPInfo_t));
+        if (!ti)
+        {
+            LogMsg("ProxyTCPAccept: cannot allocate TCPSocket");
+            close(newfd);
+            return;
+        }
+        mDNSPlatformMemZero(ti, sizeof(ProxyTCPInfo_t));
+        TCPSocket *sock = &ti->sock;
+
+        kq = &sock->ss;
+        kq->sktv4 = -1;
+        kq->sktv6 = -1;
+        kq->m = listenSet->m;
+
+        fcntl(newfd, F_SETFL, fcntl(newfd, F_GETFL, 0) | O_NONBLOCK); // set non-blocking
+        if (ss.ss_family == AF_INET)
+        {
+            s =  &kq->sktv4;
+            k =  &kq->kqsv4;
+            // Receive interface identifiers
+            err = setsockopt(newfd, IPPROTO_IP, IP_RECVIF, &on, sizeof(on));
+            if (err)
+            {
+                LogMsg("ProxyTCPAccept: IP_RECVIF %d errno %d (%s)", newfd, errno, strerror(errno));
+                mDNSPlatformDisposeProxyContext(ti);
+                return;
+            }
+        }
+        else
+        {
+            s =  &kq->sktv6;
+            k =  &kq->kqsv6;
+            // We want to receive destination addresses and receive interface identifiers
+            err = setsockopt(newfd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
+            if (err)
+            {
+                LogMsg("ProxyTCPAccept: IP_RECVPKTINFO %d errno %d (%s)", newfd, errno, strerror(errno));
+                mDNSPlatformDisposeProxyContext(ti);
+                return;
+            }
+        }
+        *s = newfd;
+        // mDNSPlatformReadTCP/WriteTCP (unlike the UDP counterpart) does not provide the destination address
+        // from which we can infer the destination address family. Hence we need to remember that here.
+        // Instead of remembering the address family, we remember the right fd.
+        sock->fd = newfd;
+        sock->kqEntry = k;
+
+        k->KQcallback = ProxyTCPSocketCallBack;
+        k->KQcontext  = ti;
+        k->KQtask     = "TCP Proxy packet reception";
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
+        k->readSource = mDNSNULL;
+        k->writeSource = mDNSNULL;
+        k->fdClosed = mDNSfalse;
+#endif
+        KQueueSet(*s, EV_ADD, EVFILT_READ, k);
+    }
+}
+
+mDNSlocal mStatus SetupUDPProxySocket(mDNS *const m, int skt, KQSocketSet *cp, u_short sa_family, mDNSBool useBackgroundTrafficClass)
+{
+    int         *s        = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6;
+    KQueueEntry *k        = (sa_family == AF_INET) ? &cp->kqsv4 : &cp->kqsv6;
+    const int on = 1;
+    mDNSIPPort port;
+    mStatus err = mStatus_NoError;
+
+    cp->m = m;
+    port = cp->port;
+    cp->closeFlag = mDNSNULL;
+
+    // set default traffic class
+    // setTrafficClass(skt, mDNSfalse);
+    (void) useBackgroundTrafficClass;
+
+    if (sa_family == AF_INET)
+    {
+        err = setsockopt(skt, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on));
+        if (err < 0)
+        {
+            LogMsg("SetupUDPProxySocket: IP_RECVDSTADDR %d errno %d (%s)", skt, errno, strerror(errno));
+            return err;
+        }
+
+        // We want to receive interface identifiers
+        err = setsockopt(skt, IPPROTO_IP, IP_RECVIF, &on, sizeof(on));
+        if (err < 0)
+        {
+            LogMsg("SetupUDPProxySocket: IP_RECVIF %d errno %d (%s)", skt, errno, strerror(errno));
+            return err;
+        }
+    }
+    else if (sa_family == AF_INET6)
+    {
+        // We want to receive destination addresses and receive interface identifiers
+        err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
+        if (err < 0)
+        {
+            LogMsg("SetupUDPProxySocket: IPV6_RECVPKTINFO %d errno %d (%s)", skt, errno, strerror(errno));
+            return err;
+        }
+
+        // We want to receive packet hop count value so we can check it
+        err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on));
+        if (err < 0)
+        {
+            LogMsg("SetupUDPProxySocket: IPV6_RECVHOPLIMIT %d errno %d (%s)", skt, errno, strerror(errno));
+            return err;
+        }
+    }
+    else
+    {
+        LogMsg("SetupUDPProxySocket: wrong family %d", sa_family);
+        return -1;
+    }
+
+    if (fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK) < 0)
+    {
+        LogMsg("SetupUDPProxySocket: fnctl failed %d", errno);
+        return -1;
+    }
+
+    *s = skt;
+    //k->KQcallback = ProxyUDPSocketCallBack;
+    k->KQcallback  = myKQSocketCallBack;
+    k->KQcontext   = cp;
+    k->KQtask      = "UDP Proxy packet reception";
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
+    k->readSource  = mDNSNULL;
+    k->writeSource = mDNSNULL;
+    k->fdClosed    = mDNSfalse;
+#endif
+
+    KQueueSet(*s, EV_ADD, EVFILT_READ, k);
+
+    return(err);
+}
+
+mDNSlocal mStatus SetupTCPProxySocket(mDNS *const m, int skt, KQSocketSet *cp, u_short sa_family, mDNSBool useBackgroundTrafficClass)
+{
+    int         *s        = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6;
+    KQueueEntry *k        = (sa_family == AF_INET) ? &cp->kqsv4 : &cp->kqsv6;
+    mDNSIPPort port;
+    mStatus err;
+
+    cp->m = m;
+    port = cp->port;
+    // XXX may not be used by the TCP codepath 
+    cp->closeFlag = mDNSNULL;
+
+    // for TCP sockets, the traffic class is set once and not changed
+    // setTrafficClass(skt, useBackgroundTrafficClass);
+    (void) useBackgroundTrafficClass;
+
+    // All the socket setup has already been done 
+    err = listen(skt, NUM_PROXY_TCP_CONNS);
+    if (err)
+    {
+        LogMsg("SetupTCPProxySocket: listen %d errno %d (%s)", skt, errno, strerror(errno));
+        return err;
+    }
+    fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK); // set non-blocking
+    
+    *s = skt;
+    k->KQcallback  = ProxyTCPAccept;
+    k->KQcontext   = cp;
+    k->KQtask      = "TCP Accept";
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
+    k->readSource  = mDNSNULL;
+    k->writeSource = mDNSNULL;
+    k->fdClosed    = mDNSfalse;
+#endif
+    KQueueSet(*s, EV_ADD, EVFILT_READ, k);
+    return mStatus_NoError;
+}
+
+mDNSlocal void BindDPSocket(int fd, int sa_family)
+{
+    int err;
+    const int on = 1;
+
+    if (sa_family == AF_INET)
+    {
+        struct sockaddr_in addr;
+
+        err = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on));
+        if (err < 0) 
+            LogMsg("BindDPSocket: setsockopt SO_REUSEPORT failed for V4 %d errno %d (%s)", fd, errno, strerror(errno));
+
+        memset(&addr, 0, sizeof(addr));
+        addr.sin_family = AF_INET;
+        addr.sin_port = htons(53);
+
+        err = bind(fd, (struct sockaddr*) &addr, sizeof(addr));
+        if (err)
+        {
+            LogMsg("BindDPSocket: bind %d errno %d (%s)", fd, errno, strerror(errno));
+            return;
+        }
+    }
+    else
+    {
+        struct sockaddr_in6 addr6;
+
+        // 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
+        err = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
+        if (err < 0)
+        {
+            LogMsg("DPFBindSocket: setsockopt IPV6_V6ONLY %d errno %d (%s)", fd, errno, strerror(errno));
+            return;
+        }
+        err = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on));
+        if (err < 0)
+            LogMsg("BindDPSocket: setsockopt SO_REUSEPORT failed for V6 %d errno %d (%s)", fd, errno, strerror(errno));
+
+        memset(&addr6, 0, sizeof(addr6));
+        addr6.sin6_family = AF_INET6;
+        addr6.sin6_port = htons(53);
+
+        err = bind(fd, (struct sockaddr*) &addr6, sizeof(addr6));
+        if (err)
+        {
+            LogMsg("BindDPSocket: bind6 %d errno %d (%s)", fd, errno, strerror(errno));
+            return;
+        }
+    }
+}
+
+// Setup DNS Proxy Skts in main kevent loop and set the skt options
+mDNSlocal void SetupDNSProxySkts(mDNS *const m, int fd[4])
+{
+    int i;
+    mStatus err;
+    KQSocketSet *udpSS;
+    KQSocketSet *tcpSS;
+
+    udpSS       = &m->p->UDPProxy.ss;
+    tcpSS       = &m->p->TCPProxy.ss;
+    udpSS->port = UnicastDNSPort;
+    tcpSS->port = UnicastDNSPort;
+
+    LogMsg("SetupDNSProxySkts: %d, %d, %d, %d", fd[0], fd[1], fd[2], fd[3]);
+
+    // myKQSocketCallBack checks for proxy and calls the m->p->ProxyCallback instead of mDNSCoreReceive
+    udpSS->proxy = mDNStrue;
+    err = SetupUDPProxySocket(m, fd[0], udpSS, AF_INET, mDNSfalse);
+    if (err)
+        LogMsg("SetupDNSProxySkts: ERROR!! UDPv4 Socket");
+
+    err = SetupUDPProxySocket(m, fd[1], udpSS, AF_INET6, mDNSfalse);
+    if (err)
+        LogMsg("SetupDNSProxySkts: ERROR!! UDPv6 Socket");
+
+    err = SetupTCPProxySocket(m, fd[2], tcpSS, AF_INET, mDNSfalse);
+    if (err)
+        LogMsg("SetupDNSProxySkts: ERROR!! TCPv4 Socket");
+
+    err = SetupTCPProxySocket(m, fd[3], tcpSS, AF_INET6, mDNSfalse);
+    if (err)
+        LogMsg("SetupDNSProxySkts: ERROR!! TCPv6 Socket");
+
+    for (i = 0; i < 4; i++)
+        dp_listener[i] = fd[i];   
+} 
+
+// Create and bind the DNS Proxy Skts for use
+mDNSexport void mDNSPlatformInitDNSProxySkts(mDNS *const m, ProxyCallback UDPCallback, ProxyCallback TCPCallback)
+{
+    int dpskt[4];
+    
+    dpskt[0] = socket(AF_INET,  SOCK_DGRAM,  IPPROTO_UDP);
+    dpskt[1] = socket(AF_INET6, SOCK_DGRAM,  IPPROTO_UDP);
+    dpskt[2] = socket(AF_INET,  SOCK_STREAM, IPPROTO_TCP);
+    dpskt[3] = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
+
+    // Close all DNS Proxy skts in case any of them are invalid
+    if (!ValidSocket(dpskt[0]) || !ValidSocket(dpskt[1]) ||
+        !ValidSocket(dpskt[2]) || !ValidSocket(dpskt[3]))
+    {   
+        if (ValidSocket(dpskt[0]))
+            close(dpskt[0]);
+        if (ValidSocket(dpskt[1]))
+            close(dpskt[1]);
+        if (ValidSocket(dpskt[2]))
+            close(dpskt[2]);
+        if (ValidSocket(dpskt[3]))
+            close(dpskt[3]);
+    }
+
+    BindDPSocket(dpskt[0], AF_INET);
+    BindDPSocket(dpskt[1], AF_INET6);
+    BindDPSocket(dpskt[2], AF_INET);
+    BindDPSocket(dpskt[3], AF_INET6);
+
+    LogInfo("mDNSPlatformInitDNSProxySkts: Opened Listener Sockets for DNS Proxy : %d, %d, %d, %d", 
+             dpskt[0], dpskt[1], dpskt[2], dpskt[3]);
+
+    m->p->UDPProxyCallback = UDPCallback;
+    m->p->TCPProxyCallback = TCPCallback;
+
+    SetupDNSProxySkts(m, dpskt);
+}
+
+mDNSexport void mDNSPlatformCloseDNSProxySkts(mDNS *const m)
+{
+    (void) m;
+    int i;
+    for (i = 0; i < 4; i++)
+        close(dp_listener[i]);
+    LogInfo("mDNSPlatformCloseDNSProxySkts: Closing DNS Proxy Listener Sockets");  
+}
+
+mDNSexport void mDNSPlatformDisposeProxyContext(void *context)
+{
+    ProxyTCPInfo_t *ti;
+    TCPSocket *sock;
+    KQSocketSet *kq;
+
+    if (!context)
+        return;
+
+    ti = (ProxyTCPInfo_t *)context;
+    sock = &ti->sock;
+
+    kq = &sock->ss;
+    if (kq->sktv4 != -1)
+    {
+        shutdown(kq->sktv4, 2);
+        mDNSPlatformCloseFD(&kq->kqsv4, kq->sktv4);
+    }
+    if (kq->sktv6 != -1)
+    {
+        shutdown(kq->sktv6, 2);
+        mDNSPlatformCloseFD(&kq->kqsv6, kq->sktv6);
+    }
+    if (kq->closeFlag)
+        *kq->closeFlag = 1;
+
+    if (ti->reply)
+        freeL("ProxyTCPInfoLen", ti->reply);
+    freeL("ProxyTCPContext", ti);
+}
+
diff --git a/mDNSMacOSX/DNSSECSupport.c b/mDNSMacOSX/DNSSECSupport.c
new file mode 100644 (file)
index 0000000..e1255c4
--- /dev/null
@@ -0,0 +1,645 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2012 Apple Computer, 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.
+ */
+
+// ***************************************************************************
+// DNSSECSupport.c: Platform specific support for DNSSEC like fetching root
+// trust anchor and dnssec probes etc.
+// ***************************************************************************
+
+#include "mDNSEmbeddedAPI.h"
+#include "DNSCommon.h"                  // For mDNS_Lock, mDNS_Random
+#include "dnssec.h"
+#include "DNSSECSupport.h"
+
+#include <CommonCrypto/CommonDigest.h>  // For Hash algorithms SHA1 etc.
+
+// Following are needed for fetching the root trust anchor dynamically
+#include <CoreFoundation/CoreFoundation.h>
+#include <libxml2/libxml/parser.h>
+#include <libxml2/libxml/tree.h>
+#include <libxml2/libxml/xmlmemory.h>
+#include <notify.h>
+
+// 30 days
+#define ROOT_TA_UPDATE_INTERVAL  (30 * 24 * 3600)   // seconds
+
+// After 100 days, the test anchors are not valid. Just an arbitrary number
+// to configure validUntil. 
+#define TEST_TA_EXPIRE_INTERVAL  (100 * 24 * 4600)
+
+// When we can't fetch the root TA due to network errors etc., we start off a timer
+// to fire at 60 seconds and then keep doubling it till we fetch it
+#define InitialTAFetchInterval 60
+
+#if !TARGET_OS_IPHONE
+DNSQuestion DNSSECProbeQuestion;
+#endif
+
+mDNSlocal int RegisterNotification(mDNS *const m, unsigned int interval);
+
+mDNSlocal void LinkTrustAnchor(mDNS *const m, TrustAnchor *ta)
+{
+    int length = 0;
+    int i;
+    mDNSu8 *p;
+    TrustAnchor **t = &m->TrustAnchors;
+    char buffer[256];
+
+    while (*t)
+        t = &((*t)->next);
+    *t = ta;
+
+    buffer[0] = 0;
+    p = ta->rds.digest;
+    for (i = 0; i < ta->digestLen; i++)
+    {
+        length += mDNS_snprintf(buffer+length, sizeof(buffer)-1-length, "%x", p[i]);
+    }
+    LogInfo("LinkTrustAnchor: Zone %##s, keytag %d, alg %d, digestType %d, digestLen %d, digest %s", ta->zone.c, ta->rds.keyTag,
+        ta->rds.alg, ta->rds.digestType, ta->digestLen, buffer);
+}
+
+mDNSlocal void DelTrustAnchor(mDNS *const m, const domainname *zone)
+{
+    TrustAnchor **ta = &m->TrustAnchors;
+    TrustAnchor *tmp;
+
+    while (*ta && !SameDomainName(&(*ta)->zone, zone))
+        ta = &(*ta)->next;
+
+    // First time, we won't find the TrustAnchor in the list as it has
+    // not been added.
+    if (!(*ta))
+        return;
+
+    tmp = *ta;
+    *ta = (*ta)->next;                  // Cut this record from the list
+    tmp->next = mDNSNULL;
+    if (tmp->rds.digest)
+        mDNSPlatformMemFree(tmp->rds.digest);
+    mDNSPlatformMemFree(tmp);
+}
+
+mDNSlocal void AddTrustAnchor(mDNS *const m, const domainname *zone, mDNSu16 keytag, mDNSu8 alg, mDNSu8 digestType, int diglen,
+    mDNSu8 *digest)
+{
+    TrustAnchor *ta, *tmp;
+    mDNSu32 t = (mDNSu32) time(NULL); 
+
+    // Check for duplicates
+    tmp = m->TrustAnchors;
+    while (tmp)
+    {
+        if (SameDomainName(zone, &tmp->zone) && tmp->rds.keyTag == keytag && tmp->rds.alg == alg && tmp->rds.digestType == digestType &&
+            !memcmp(tmp->rds.digest, digest, diglen))
+        {
+            LogMsg("AddTrustAnchors: Found a duplicate");
+            return;
+        }
+        tmp = tmp->next;
+    }
+
+    ta = (TrustAnchor *)mDNSPlatformMemAllocate(sizeof(TrustAnchor));
+    if (!ta)
+    {
+        LogMsg("AddTrustAnchor: malloc failure ta");
+        return;
+    }
+    ta->rds.keyTag = keytag;
+    ta->rds.alg = alg;
+    ta->rds.digestType = digestType;
+    ta->rds.digest = digest;
+    ta->digestLen = diglen;
+    ta->validFrom = t;
+    ta->validUntil = t + TEST_TA_EXPIRE_INTERVAL;
+    AssignDomainName(&ta->zone, zone);
+    ta->next = mDNSNULL;
+
+    LinkTrustAnchor(m, ta);
+}
+
+#define HexVal(X) ( ((X) >= '0' && (X) <= '9') ? ((X) - '0'     ) :   \
+                    ((X) >= 'A' && (X) <= 'F') ? ((X) - 'A' + 10) :   \
+                    ((X) >= 'a' && (X) <= 'f') ? ((X) - 'a' + 10) : -1)
+
+mDNSlocal mDNSu8 *ConvertDigest(char *digest, int digestType, int *diglen)
+{
+    int i, j;
+    mDNSu8 *dig;
+
+    switch (digestType)
+    {
+    case SHA1_DIGEST_TYPE:
+        *diglen = CC_SHA1_DIGEST_LENGTH;
+        break;
+    case SHA256_DIGEST_TYPE:
+        *diglen = CC_SHA256_DIGEST_LENGTH;
+        break;
+    default:
+        LogMsg("ConvertDigest: digest type %d not supported", digestType);
+        return mDNSNULL;
+    }
+    dig = mDNSPlatformMemAllocate(*diglen);
+    if (!dig)
+    {
+        LogMsg("ConvertDigest: malloc failure");
+        return mDNSNULL;
+    }
+
+    for (j=0,i=0; i<*diglen*2; i+=2)
+    {
+        int l, h;
+        l = HexVal(digest[i]);
+        h = HexVal(digest[i+1]);
+        if (l<0 || h<0) { LogMsg("ConvertDigest: Cannot convert digest"); return NULL;}
+        dig[j++] = (mDNSu8)((l << 4) | h);
+    }
+    return dig;
+}
+
+// All the children are in a linked list
+//
+// <TrustAnchor> has two children: <Zone> and <KeyDigest>
+// <KeyDigest> has four children <KeyTag> <Algorithm> <DigestType> <Digest>
+//
+// Returns false if failed to parse the element i.e., malformed xml document.
+// Validity of the actual values itself is done outside the function.
+mDNSlocal mDNSBool ParseElementChildren(xmlDocPtr tadoc, xmlNode *node, TrustAnchor *ta)
+{
+    xmlNode *cur_node;
+    xmlChar *val1, *val2, *val;
+    char *invalid = NULL;
+
+    val = val1 = val2 = NULL;
+
+    for (cur_node = node; cur_node; cur_node = cur_node->next)
+    {
+        invalid = NULL;
+        val1 = val2 = NULL;
+        
+        val = xmlNodeListGetString(tadoc, cur_node->xmlChildrenNode, 1);
+        if (!val)
+        {
+           LogInfo("ParseElementChildren: NULL value for %s", cur_node->name);
+           continue; 
+        }
+        if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Zone"))
+        {
+            // MaeDomainNameFromDNSNameString does not work for "."
+            if (!xmlStrcmp(val, (const xmlChar *)"."))
+            {
+                ta->zone.c[0] = 0;
+            }
+            else if (!MakeDomainNameFromDNSNameString(&ta->zone, (char *)val))
+            {
+                LogMsg("ParseElementChildren: Cannot parse Zone %s", val);
+                goto error;
+            }
+            else
+            {
+                LogInfo("ParseElementChildren: Element %s, value %##s", cur_node->name, ta->zone.c);
+            }
+        }
+        else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"KeyTag"))
+        {
+            ta->rds.keyTag = strtol((const char *)val, &invalid, 10);
+            if (*invalid != '\0')
+            {
+                LogMsg("ParseElementChildren: KeyTag invalid character %d", *invalid);
+                goto error;
+            }
+            else
+            {
+                LogInfo("ParseElementChildren: Element %s, value %d", cur_node->name, ta->rds.keyTag);
+            }
+        }
+        else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Algorithm"))
+        {
+            ta->rds.alg = strtol((const char *)val, &invalid, 10);
+            if (*invalid != '\0')
+            {
+                LogMsg("ParseElementChildren: Algorithm invalid character %c", *invalid);
+                goto error;
+            }
+            else
+            {
+                LogInfo("ParseElementChildren: Element %s, value %d", cur_node->name, ta->rds.alg);
+            }
+        }
+        else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"DigestType"))
+        {
+            ta->rds.digestType = strtol((const char *)val, &invalid, 10);
+            if (*invalid != '\0')
+            {
+                LogMsg("ParseElementChildren: Algorithm invalid character %c", *invalid);
+                goto error;
+            }
+            else
+            {
+                LogInfo("ParseElementChildren: Element %s, value %d", cur_node->name, ta->rds.digestType);
+            }
+        }
+        else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Digest"))
+        {
+            int diglen;
+            mDNSu8 *dig = ConvertDigest((char *)val, ta->rds.digestType, &diglen);
+            if (dig)
+            { 
+                LogInfo("ParseElementChildren: Element %s, digest %s", cur_node->name, val);
+                ta->digestLen = diglen;
+                ta->rds.digest = dig;
+            }
+            else
+            {
+                LogMsg("ParseElementChildren: Element %s, NULL digest", cur_node->name);
+                goto error;
+            }
+        }
+        else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"KeyDigest"))
+        {
+            struct tm tm;
+            val1 = xmlGetProp(cur_node, (const xmlChar *)"validFrom");
+            if (val1)
+            {
+                char *s = strptime((const char *)val1, "%Y-%m-%dT%H:%M:%S", &tm);
+                if (!s)
+                {
+                    LogMsg("ParseElementChildren: Parsing ValidFrom failed %s", val1);
+                    goto error;
+                }
+                else
+                {
+                    ta->validFrom = (mDNSu32)timegm(&tm);
+                }
+            }
+            val2 = xmlGetProp(cur_node, (const xmlChar *)"validUntil");
+            if (val2)
+            {
+                char *s = strptime((const char *)val2, "%Y-%m-%dT%H:%M:%S", &tm);
+                if (!s)
+                {
+                    LogMsg("ParseElementChildren: Parsing ValidFrom failed %s", val2);
+                    goto error;
+                }
+                else
+                {
+                    ta->validUntil = (mDNSu32)timegm(&tm);
+                }
+            }
+            else
+            {
+                // If there is no validUntil, set it to the next probing interval
+                mDNSu32 t = (mDNSu32) time(NULL); 
+                ta->validUntil = t + ROOT_TA_UPDATE_INTERVAL;
+            }
+            LogInfo("ParseElementChildren: ValidFrom time %u, validUntil %u", (unsigned)ta->validFrom, (unsigned)ta->validUntil);
+        }
+        if (val1)
+            xmlFree(val1);
+        if (val2)
+            xmlFree(val2);
+        if (val)
+            xmlFree(val);
+    }
+    return mDNStrue;
+error:
+    if (val1)
+        xmlFree(val1);
+    if (val2)
+        xmlFree(val2);
+    if (val)
+        xmlFree(val);
+    return mDNSfalse;
+}
+
+mDNSlocal mDNSBool ValidateTrustAnchor(TrustAnchor *ta)
+{
+    time_t currTime = time(NULL);
+
+    // Currently only support trust anchor for root.
+    if (!SameDomainName(&ta->zone, (const domainname *)"\000"))
+    {
+        LogInfo("ParseElementChildren: Zone %##s not root", ta->zone.c);
+        return mDNSfalse;
+    }
+
+    switch (ta->rds.digestType)
+    {
+    case SHA1_DIGEST_TYPE:
+        if (ta->digestLen != CC_SHA1_DIGEST_LENGTH) 
+        {
+            LogMsg("ValidateTrustAnchor: Invalid digest len %d for SHA1", ta->digestLen);
+            return mDNSfalse;
+        }
+        break;
+    case SHA256_DIGEST_TYPE:
+        if (ta->digestLen != CC_SHA256_DIGEST_LENGTH) 
+        {
+            LogMsg("ValidateTrustAnchor: Invalid digest len %d for SHA256", ta->digestLen);
+            return mDNSfalse;
+        }
+        break;
+    default:
+        LogMsg("ValidateTrustAnchor: digest type %d not supported", ta->rds.digestType);
+        return mDNSfalse;
+    }
+    if (!ta->rds.digest)
+    {
+        LogMsg("ValidateTrustAnchor: digest NULL for %d", ta->rds.digestType);
+        return mDNSfalse;
+    }
+    switch (ta->rds.alg)
+    {
+    case CRYPTO_RSA_SHA512:
+    case CRYPTO_RSA_SHA256:
+    case CRYPTO_RSA_NSEC3_SHA1:
+    case CRYPTO_RSA_SHA1:
+        break;
+    default:
+        LogMsg("ValidateTrustAnchor: Algorithm %d not supported", ta->rds.alg);
+        return mDNSfalse;
+    }
+    
+    if (DNS_SERIAL_LT(currTime, ta->validFrom))
+    {
+        LogMsg("ValidateTrustAnchor: Invalid ValidFrom time %u, currtime %u", (unsigned)ta->validFrom, (unsigned)currTime);
+        return mDNSfalse;
+    }
+    if (DNS_SERIAL_LT(ta->validUntil, currTime))
+    {
+        LogMsg("ValidateTrustAnchor: Invalid ValidUntil time %u, currtime %u", (unsigned)ta->validUntil, (unsigned)currTime);
+        return mDNSfalse;
+    }
+    return mDNStrue;
+}
+
+mDNSlocal mDNSBool ParseElement(xmlDocPtr tadoc, xmlNode * a_node, TrustAnchor *ta)
+{
+    xmlNode *cur_node = NULL;
+
+    for (cur_node = a_node; cur_node; cur_node = cur_node->next)
+    {
+        if (cur_node->type == XML_ELEMENT_NODE)
+        {
+            // There could be multiple KeyDigests per TrustAnchor. We keep parsing till we
+            // reach the last one or we encounter an error in parsing the document.
+            if (!xmlStrcmp(cur_node->name, (const xmlChar *)"KeyDigest"))
+            {
+                if (ta->rds.digest)
+                    mDNSPlatformMemFree(ta->rds.digest);
+                ta->rds.digestType = 0;
+                ta->digestLen = 0;
+            }
+            if (!ParseElementChildren(tadoc, cur_node->children, ta))
+                return mDNSfalse;
+            if (!ParseElement(tadoc, cur_node->children, ta))
+                return mDNSfalse;
+        }
+    }
+    return mDNStrue;
+}
+
+mDNSlocal void TAComplete(mDNS *const m, void *context)
+{
+    TrustAnchor *ta = (TrustAnchor *)context;
+
+    DelTrustAnchor(m, &ta->zone);
+    LinkTrustAnchor(m, ta);
+}
+
+mDNSlocal void FetchRootTA(mDNS *const m)
+{
+    CFStringRef urlString = CFSTR("https://data.iana.org/root-anchors/root-anchors.xml");
+    CFDataRef xmlData;
+    CFStringRef fileRef = NULL;
+    const char *xmlFileName = NULL;
+    char buf[512];
+    CFURLRef url = NULL;
+    static unsigned int RootTAFetchInterval = InitialTAFetchInterval;
+
+    (void) m;
+
+    TrustAnchor *ta = (TrustAnchor *)mDNSPlatformMemAllocate(sizeof(TrustAnchor));
+    if (!ta)
+    {
+        LogMsg("FetchRootTA: TrustAnchor alloc failed");
+        return;
+    }
+    memset(ta, 0, sizeof(TrustAnchor));
+
+    url = CFURLCreateWithString(NULL, urlString, NULL);
+    if (!url)
+    {
+        LogMsg("FetchRootTA: CFURLCreateWithString error");
+        mDNSPlatformMemFree(ta);
+        return;
+    }
+
+    // If we can't fetch the XML file e.g., network problems, trigger a timer. All other failures
+    // should hardly happen in practice for which schedule the normal interval to refetch the TA.
+    if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, url, &xmlData, NULL, NULL, NULL))
+    {
+        LogInfo("FetchRootTA: CFURLCreateDataAndPropertiesFromResource error");
+        CFRelease(url);
+        mDNSPlatformMemFree(ta);
+        RegisterNotification(m, RootTAFetchInterval);
+        RootTAFetchInterval *= 2 + 1;
+        return;
+    }
+
+    // get the name of the last component from the url, libxml will use it if
+    // it has to report an error
+    fileRef = CFURLCopyLastPathComponent(url);
+    if (fileRef)
+    {
+        xmlFileName = CFStringGetCStringPtr(fileRef, kCFStringEncodingUTF8);
+        if (!xmlFileName)
+        {
+            if (!CFStringGetCString(fileRef, buf, sizeof(buf), kCFStringEncodingUTF8) )
+                strlcpy(buf, "nofile.xml", sizeof(buf));
+            xmlFileName = (const char *)buf;
+        }
+    }
+
+    // Parse the XML and get the CFXMLTree.
+    xmlDocPtr tadoc = xmlReadMemory((const char*)CFDataGetBytePtr(xmlData),
+        (int)CFDataGetLength(xmlData), xmlFileName, NULL, 0);        
+
+    CFRelease(fileRef);
+    CFRelease(url);
+    CFRelease(xmlData);
+
+    if (!tadoc)
+    {
+        LogMsg("FetchRootTA: xmlReadMemory failed");
+        goto done;
+    }
+
+    xmlNodePtr root = xmlDocGetRootElement(tadoc);
+    if (!root)
+    {
+        LogMsg("FetchRootTA: Cannot get root element");
+        goto done;
+    }
+
+    if (ParseElement(tadoc, root, ta) && ValidateTrustAnchor(ta))
+    {
+        // Do the actual addition of TA on the main queue.
+        mDNSPlatformDispatchAsync(m, ta, TAComplete);
+    }
+    else
+    {
+        if (ta->rds.digest)
+            mDNSPlatformMemFree(ta->rds.digest);
+        mDNSPlatformMemFree(ta);
+    }
+done:
+    if (tadoc)
+        xmlFreeDoc(tadoc);
+    RegisterNotification(m, ROOT_TA_UPDATE_INTERVAL);
+    RootTAFetchInterval = InitialTAFetchInterval;
+    return;
+}
+
+
+#if APPLE_OSX_mDNSResponder && !TARGET_OS_IPHONE
+mDNSlocal void DNSSECProbeCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+{
+    if (!AddRecord)
+        return;
+
+    mDNS_Lock(m);
+    if ((m->timenow - question->StopTime) >= 0)
+    {
+        mDNS_Unlock(m);
+        LogDNSSEC("DNSSECProbeCallback: Question %##s (%s) timed out", question->qname.c, DNSTypeName(question->qtype));
+        mDNS_StopQuery(m, question);
+        return;
+    }
+    mDNS_Unlock(m);
+
+    // Wait till we get the DNSSEC results. If we get a negative response e.g., no DNS servers, the
+    // question will be restarted by the core and we should have the DNSSEC results eventually.
+    if (AddRecord != QC_dnssec)
+    {
+        LogDNSSEC("DNSSECProbeCallback: Question %##s (%s)", question->qname.c, DNSTypeName(question->qtype), RRDisplayString(m, answer));
+        return;
+    }
+
+    LogDNSSEC("DNSSECProbeCallback: Question %##s (%s), DNSSEC status %s", question->qname.c, DNSTypeName(question->qtype),
+            DNSSECStatusName(question->ValidationStatus));
+
+    mDNS_StopQuery(m, question);
+}
+
+// Send a DNSSEC probe just for the sake of collecting DNSSEC statistics.
+mDNSexport void DNSSECProbe(mDNS *const m)
+{
+    mDNSu32 rand;
+
+    if (DNSSECProbeQuestion.ThisQInterval != -1)
+        return;
+    
+    rand = mDNSRandom(0x3FFFFFFF) % 100;
+    // Probe 5% of the time
+    if (rand > 5)
+        return;
+    
+    mDNS_DropLockBeforeCallback();
+    InitializeQuestion(m, &DNSSECProbeQuestion, mDNSInterface_Any, (const domainname *)"\003com", kDNSType_DS, DNSSECProbeCallback, mDNSNULL);
+    DNSSECProbeQuestion.ValidatingResponse = 0;
+    DNSSECProbeQuestion.ValidationRequired = DNSSEC_VALIDATION_SECURE;
+
+    BumpDNSSECStats(m, kStatsActionIncrement, kStatsTypeProbe, 1);
+    mDNS_StartQuery(m, &DNSSECProbeQuestion);
+    mDNS_ReclaimLockAfterCallback(); 
+}
+#endif // APPLE_OSX_mDNSResponder && !TARGET_OS_IPHONE
+
+// For now we fetch the root trust anchor and update the local copy
+mDNSexport void UpdateTrustAnchors(mDNS *const m)
+{
+    // Register for a notification to fire immediately which in turn will update
+    // the trust anchor
+    if (RegisterNotification(m, 1))
+    {
+        LogMsg("UpdateTrustAnchors: ERROR!! failed to register for notification");
+    }
+}
+
+mDNSlocal int RegisterNotification(mDNS *const m, unsigned int interval)
+{
+    int len = strlen("com.apple.system.notify.service.timer:+") + 21; // 21 bytes to accomodate the interval
+    char buffer[len];
+    unsigned int blen;
+    int status;
+
+    // Starting "interval" second from now (+ below indicates relative) register for a notification
+    blen = mDNS_snprintf(buffer, sizeof(buffer), "com.apple.system.notify.service.timer:+%us", interval);
+    if (blen >= sizeof(buffer))
+    {
+        LogMsg("RegisterNotification: Buffer too small blen %d, buffer size %d", blen, sizeof(buffer));
+        return -1;
+    } 
+    LogInfo("RegisterNotification: buffer %s", buffer);
+    if (m->notifyToken)
+    {
+        notify_cancel(m->notifyToken);
+        m->notifyToken = 0;
+    }
+    status = notify_register_dispatch(buffer, &m->notifyToken,
+                dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
+                ^(int t) { (void) t; FetchRootTA(m); });
+
+    if (status != NOTIFY_STATUS_OK)
+    {
+        LogMsg("RegisterNotification: notify_register_dispatch failed");
+        return -1;
+    }
+    return 0;
+}
+
+mDNSexport mStatus DNSSECPlatformInit(mDNS *const m)
+{
+    int diglen;
+
+    m->TrustAnchors = mDNSNULL;
+    m->notifyToken  = 0;
+
+    // Add a couple of trust anchors for testing purposes.
+    mDNSlocal const domainname *testZone  = (const domainname*)"\007example";
+
+    char *digest = "F122E47B5B7D2B6A5CC0A21EADA11D96BB9CC927";
+    mDNSu8 *dig = ConvertDigest(digest, 1, &diglen);
+    AddTrustAnchor(m, testZone, 23044, 5, 1, diglen, dig);
+
+    char *digest1 = "D795AE5E1AFB200C6139474199B70EAD3F3484553FD97BE5A43704B8A4791F21";
+    dig = ConvertDigest(digest1, 2, &diglen);
+    AddTrustAnchor(m, testZone, 23044, 5, 2, diglen, dig);
+
+    // Add the TA for root zone manually here. We will dynamically fetch the root TA and
+    // update it shortly. If that fails e.g., disconnected from the network, we still
+    // have something to work with.
+    char *digest2 = "49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5";
+    dig = ConvertDigest(digest2, 2, &diglen);
+    AddTrustAnchor(m, (const domainname *)"\000", 19036, 8, 2, diglen, dig);
+
+#if !TARGET_OS_IPHONE
+    DNSSECProbeQuestion.ThisQInterval = -1;
+#endif
+    return mStatus_NoError;
+}
diff --git a/mDNSMacOSX/DNSSECSupport.h b/mDNSMacOSX/DNSSECSupport.h
new file mode 100644 (file)
index 0000000..2310fc2
--- /dev/null
@@ -0,0 +1,24 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2012 Apple Computer, 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 __DNSSEC_SUPPORT_H
+#define __DNSSEC_SUPPORT_H
+
+extern mStatus DNSSECPlatformInit(mDNS *const m);
+extern void UpdateTrustAnchors(mDNS *const m);
+
+#endif // __DNSSEC_SUPPORT_H
index e6297320014a7250e90d83c2aa3dd4420a4ee7bd..717efca2f739e426de1a8562d767e6fc9c204ede 100644 (file)
@@ -23,7 +23,7 @@
 #undef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3
 #define AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3
 
 #undef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3
 #define AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3
 
-#include "DNSServiceDiscovery.h"
+#include "../mDNSMacOSX/DNSServiceDiscovery.h"
 #include "DNSServiceDiscoveryDefines.h"
 #include "DNSServiceDiscoveryReplyServer.h"
 
 #include "DNSServiceDiscoveryDefines.h"
 #include "DNSServiceDiscoveryReplyServer.h"
 
index 9a686d8d1f73009719374dbda3b6f6ef5d30f452..a21868c7515e4a9f9e2b61aa9d6624240bf4adcc 100644 (file)
@@ -19,5 +19,7 @@
   <true/>
   <key>BeginTransactionAtShutdown</key>
   <true/>
   <true/>
   <key>BeginTransactionAtShutdown</key>
   <true/>
+  <key>POSIXSpawnType</key>
+  <string>Interactive</string>
 </dict>
 </plist>
 </dict>
 </plist>
index 8966dc093d706453951956a84fa13787ead558be..ba99d15d70b44dbc60ade86239095274269f0bb8 100644 (file)
        <key>ProgramArguments</key>
        <array>
                <string>/usr/sbin/mDNSResponder</string>
        <key>ProgramArguments</key>
        <array>
                <string>/usr/sbin/mDNSResponder</string>
-               <string>-launchd</string>
        </array>
        <key>MachServices</key>
        <dict>
                <key>com.apple.mDNSResponder</key>
                <true/>
        </array>
        <key>MachServices</key>
        <dict>
                <key>com.apple.mDNSResponder</key>
                <true/>
+               <key>com.apple.mDNSResponder.dnsproxy</key>
+               <true/>
        </dict>
        <key>Sockets</key>
        <dict>
        </dict>
        <key>Sockets</key>
        <dict>
index 8e03d6506112a520f59e09042725a9bf5c5e7c61..e7cd65f0beed31a012b2f6a0df6466abd95de53e 100644 (file)
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 4 -*-
  *
 /* -*- Mode: C; tab-width: 4 -*-
  *
- * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2004-2013 Apple Computer, 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.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -364,7 +364,7 @@ mDNSlocal void handleLNTPortMappingResponse(tcpLNTInfo *tcpInfo)
         // Make sure to compute extport *before* we zero tcpInfo->retries
         extport = mDNSOpaque16fromIntVal(RequestedPortNum(natInfo));
         tcpInfo->retries = 0;
         // Make sure to compute extport *before* we zero tcpInfo->retries
         extport = mDNSOpaque16fromIntVal(RequestedPortNum(natInfo));
         tcpInfo->retries = 0;
-        natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, mStatus_NoError, extport, NATMAP_DEFAULT_LEASE);
+        natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, mStatus_NoError, extport, NATMAP_DEFAULT_LEASE, NATTProtocolUPNPIGD);
     }
     else if (http_result == HTTPCode_500)
     {
     }
     else if (http_result == HTTPCode_500)
     {
@@ -382,7 +382,7 @@ mDNSlocal void handleLNTPortMappingResponse(tcpLNTInfo *tcpInfo)
                 {
                     LogMsg("handleLNTPortMappingResponse too many conflict retries %d %d", mDNSVal16(natInfo->IntPort), mDNSVal16(natInfo->RequestedPort));
                     mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "Conflict - too many retries", "Retries: %d", tcpInfo->retries);
                 {
                     LogMsg("handleLNTPortMappingResponse too many conflict retries %d %d", mDNSVal16(natInfo->IntPort), mDNSVal16(natInfo->RequestedPort));
                     mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "Conflict - too many retries", "Retries: %d", tcpInfo->retries);
-                    natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0);
+                    natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0, NATTProtocolUPNPIGD);
                 }
                 return;
             }
                 }
                 return;
             }
@@ -485,8 +485,8 @@ exit:
                 mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "success", "success", "");
             break;
         case LNTExternalAddrOp:  mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest",
                 mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "success", "success", "");
             break;
         case LNTExternalAddrOp:  mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest",
-                                            mDNSIPv4AddressIsZero(m->ExternalAddress) ? "failure" : "success",
-                                            mDNSIPv4AddressIsZero(m->ExternalAddress) ? "failure" : "success", "");
+                                            mDNSIPv4AddressIsZero(m->ExtAddress) ? "failure" : "success",
+                                            mDNSIPv4AddressIsZero(m->ExtAddress) ? "failure" : "success", "");
             break;
         case LNTPortMapOp:       if (tcpInfo->parentNATInfo)
                 mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", (tcpInfo->parentNATInfo->Result) ? "failure" : "success",
             break;
         case LNTPortMapOp:       if (tcpInfo->parentNATInfo)
                 mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", (tcpInfo->parentNATInfo->Result) ? "failure" : "success",
@@ -658,7 +658,7 @@ mDNSlocal mStatus SendPortMapRequest(mDNS *m, NATTraversalInfo *n)
             }
             else
             {
             }
             else
             {
-                natTraversalHandlePortMapReply(m, n, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0);
+                natTraversalHandlePortMapReply(m, n, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0, NATTProtocolUPNPIGD);
                 return mStatus_NoError;
             }
         }
                 return mStatus_NoError;
             }
         }
@@ -911,7 +911,7 @@ mDNSexport void LNT_SendDiscoveryMsg(mDNS *m)
     // Create message
     bufLen = mDNS_snprintf((char*)buf, sizeof(m->omsg), msg, m->SSDPWANPPPConnection ? "PPP" : "IP");
 
     // Create message
     bufLen = mDNS_snprintf((char*)buf, sizeof(m->omsg), msg, m->SSDPWANPPPConnection ? "PPP" : "IP");
 
-    debugf("LNT_SendDiscoveryMsg Router %.4a Current External Address %.4a", &m->Router.ip.v4, &m->ExternalAddress);
+    debugf("LNT_SendDiscoveryMsg Router %.4a Current External Address %.4a", &m->Router.ip.v4, &m->ExtAddress);
 
     if (!mDNSIPv4AddressIsZero(m->Router.ip.v4))
     {
 
     if (!mDNSIPv4AddressIsZero(m->Router.ip.v4))
     {
diff --git a/mDNSMacOSX/Private/dns_services.c b/mDNSMacOSX/Private/dns_services.c
new file mode 100644 (file)
index 0000000..d0e9e6c
--- /dev/null
@@ -0,0 +1,212 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2012 Apple Inc. All rights reserved.
+ *
+ * PRIVATE DNSX CLIENT LIBRARY --FOR Apple Platforms ONLY OSX/iOS--
+ * Resides in /usr/lib/libdns_services.dylib
+ */
+
+#include "dns_services.h"
+#include "dns_xpc.h"
+#include <xpc/xpc.h>
+#include <Block.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+
+//*************************************************************************************************************
+// Globals
+
+#define connection_t xpc_connection_t
+
+struct _DNSXConnRef_t
+{
+    connection_t      conn_ref;      // xpc_connection between client and daemon
+    dispatch_queue_t  lib_q;         // internal queue created in library itself   
+    void              *AppCallBack;  // Callback function ptr for Client
+    dispatch_queue_t  client_q;      // Queue specified by client for scheduling its Callback
+};
+
+//*************************************************************************************************************
+// Utility Functions
+
+static bool LogDebugEnabled()
+{
+    return false;
+}
+
+static void LogDebug(const char *prefix, xpc_object_t o)
+{
+    if (!LogDebugEnabled()) 
+        return;
+    
+    char *desc = xpc_copy_description(o);
+    syslog(LOG_INFO, "%s: %s", prefix, desc); 
+    free(desc);
+}
+
+//**************************************************************************************************************
+
+void DNSXRefDeAlloc(DNSXConnRef connRef)
+{
+    if (!connRef)
+    {
+        syslog(LOG_WARNING, "dns_services: DNSXRefDeAlloc called with NULL DNSXConnRef");
+        return;
+    }
+
+    // Schedule this work on the internal library queue
+    dispatch_sync(connRef->lib_q, ^{
+
+        xpc_release(connRef->conn_ref);
+        connRef->AppCallBack = NULL;
+        dispatch_release(connRef->client_q);
+
+    });
+
+    dispatch_release(connRef->lib_q);
+    free(connRef);
+
+    syslog(LOG_INFO, "dns_services: DNSXRefDeAlloc successfully DeAllocated connRef");
+
+}
+
+// Sends the Msg(Dictionary) to the Server
+static DNSXErrorType SendMsgToServer(DNSXConnRef *connRef, xpc_object_t msg, bool old_conn)
+{
+    DNSXErrorType errx = kDNSX_NoError;
+
+    LogDebug("dns_services: SendMsgToServer", msg);
+    
+    xpc_connection_set_event_handler((*connRef)->conn_ref, ^(xpc_object_t recv_msg)
+    {
+        xpc_type_t type = xpc_get_type(recv_msg);
+
+        if (type == XPC_TYPE_DICTIONARY)
+        {
+            LogDebug("dns_services: SendMsgToServer SUCCESS CALLBACK FROM SERVER", recv_msg);
+            syslog(LOG_INFO, "dns_services: Successfully Sent Msg to the Daemon");
+            uint64_t daemon_status = xpc_dictionary_get_uint64(recv_msg, kDNSDaemonReply);
+            // Schedule the AppCallBacks on the Client Specified Queue
+            switch (daemon_status)
+            {   
+                case kDNSDaemonEngaged:
+                        dispatch_async((*connRef)->client_q, ^{  
+                                        ((DNSXEnableProxyReply)(*connRef)->AppCallBack)((*connRef), kDNSX_Engaged);
+                                        }); 
+                                        break;
+                case kDNSMsgReceived:
+                        dispatch_async((*connRef)->client_q, ^{
+                                        ((DNSXEnableProxyReply)(*connRef)->AppCallBack)((*connRef), kDNSX_NoError);
+                                        }); 
+                                        break;
+                default:
+                        dispatch_async((*connRef)->client_q, ^{
+                                        ((DNSXEnableProxyReply)(*connRef)->AppCallBack)((*connRef), kDNSX_UnknownErr);
+                                        }); 
+                                        break;
+            }   
+
+        }
+        else
+        {
+            LogDebug("dns_services: SendMsgToServer UNEXPECTED CALLBACK FROM SERVER", recv_msg);
+            syslog(LOG_WARNING, "dns_services: Connection failed since NO privileges to access service OR Daemon NOT Running");
+            dispatch_async((*connRef)->client_q, ^{
+                            ((DNSXEnableProxyReply)(*connRef)->AppCallBack)((*connRef), kDNSX_DaemonNotRunning);
+                            });
+        }
+    });
+    
+    // To prevent Over-Resume of a connection
+    if (!old_conn)
+        xpc_connection_resume((*connRef)->conn_ref);
+    xpc_connection_send_message((*connRef)->conn_ref, msg);
+    if (!errx)
+        syslog(LOG_INFO, "dns_services: SendMSgToServer sent Msg Dict successfully to Daemon");
+    return errx;
+}
+
+// Creates a new DNSX Connection Reference(DNSXConnRef).
+// If DNSXConnRef exists, you may want to use that depending on the use case
+static DNSXErrorType InitConnection(DNSXConnRef *connRef, const char *servname, dispatch_queue_t clientq, void *AppCallBack)
+{
+    if (!connRef)
+    {
+        syslog(LOG_WARNING, "dns_services: InitConnection() called with NULL DNSXConnRef");
+        return kDNSX_BadParam;   
+    }
+
+    *connRef = malloc(sizeof(struct _DNSXConnRef_t));
+    if (!(*connRef))
+    {
+        syslog(LOG_WARNING, "dns_services: InitConnection() No memory to allocate");
+        return kDNSX_NoMem;
+    }
+
+    // Initialize the DNSXConnRef  
+    dispatch_retain(clientq);
+    (*connRef)->client_q     = clientq;
+    (*connRef)->AppCallBack  = AppCallBack;    
+    (*connRef)->lib_q        = dispatch_queue_create("com.apple.mDNSResponder.libdns_services.q", NULL); 
+    (*connRef)->conn_ref     = xpc_connection_create_mach_service(servname, (*connRef)->lib_q, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED);
+
+    syslog(LOG_INFO, "dns_services: InitConnection() successfully create a new DNSXConnRef");
+    return kDNSX_NoError;
+}
+
+DNSXErrorType DNSXEnableProxy(DNSXConnRef *connRef, DNSProxyParameters proxyparam, IfIndex inIfindexArr[MaxInputIf], 
+                               IfIndex outIfindex, dispatch_queue_t clientq, DNSXEnableProxyReply callBack)
+{
+
+    DNSXErrorType errx = kDNSX_NoError;
+    bool old_conn = false;    
+
+    // Sanity Checks
+    if (!connRef || !callBack || !clientq)
+    {
+        syslog(LOG_WARNING, "dns_services: DNSXEnableProxy called with NULL DNSXConnRef OR Callback OR ClientQ parameter");
+        return kDNSX_BadParam;
+    }   
+
+    // If no connRef, get it from InitConnection()
+    if (!*connRef)
+    {
+        errx = InitConnection(connRef, kDNSProxyService, clientq, callBack);
+        if (errx) // On error InitConnection() leaves *connRef set to NULL
+        {
+            syslog(LOG_WARNING, "dns_services: Since InitConnection() returned %d error returning w/o sending msg", errx);
+            return errx;
+        }
+    }
+    else // Client already has a valid connRef
+    {
+        old_conn = true;
+    }
+
+    // Create Dictionary To Send
+    xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); 
+    if (!dict)
+    {
+        syslog(LOG_WARNING, "dns_services: DNSXEnableProxy could not create the Msg Dict To Send!");
+        DNSXRefDeAlloc(*connRef);
+        return kDNSX_DictError;
+    }
+
+    xpc_dictionary_set_uint64(dict, kDNSProxyParameters, proxyparam);
+
+    xpc_dictionary_set_uint64(dict, kDNSInIfindex0,      inIfindexArr[0]);
+    xpc_dictionary_set_uint64(dict, kDNSInIfindex1,      inIfindexArr[1]);
+    xpc_dictionary_set_uint64(dict, kDNSInIfindex2,      inIfindexArr[2]); 
+    xpc_dictionary_set_uint64(dict, kDNSInIfindex3,      inIfindexArr[3]);
+    xpc_dictionary_set_uint64(dict, kDNSInIfindex4,      inIfindexArr[4]);
+
+    xpc_dictionary_set_uint64(dict, kDNSOutIfindex,      outIfindex);
+    errx = SendMsgToServer(connRef, dict, old_conn);
+    xpc_release(dict);
+
+    return errx; 
+}
+
diff --git a/mDNSMacOSX/Private/dns_services.h b/mDNSMacOSX/Private/dns_services.h
new file mode 100644 (file)
index 0000000..7b74e10
--- /dev/null
@@ -0,0 +1,124 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2012 Apple Inc. All rights reserved.
+ *
+ *
+ * @header      Interface to DNSX SPI
+ *
+ * @discussion  Describes the functions and data structures
+ *              that make up the DNSX SPI
+ */
+
+#ifndef _DNS_SERVICES_H
+#define _DNS_SERVICES_H
+
+#include <dispatch/dispatch.h>
+
+// DNSXConnRef: Opaque internal data type
+typedef struct _DNSXConnRef_t *DNSXConnRef;
+
+typedef enum
+{
+    kDNSX_NoError                   =  0,
+    kDNSX_UnknownErr                = -65537,   /* 0xFFFE FFFF */
+    kDNSX_NoMem                     = -65539,
+    kDNSX_BadParam                  = -65540,
+    kDNSX_DaemonNotRunning          = -65563,   /* Background daemon not running */
+    kDNSX_DictError                 = -65565,   /* Dictionary Error */
+    kDNSX_Engaged                   = -65566,   /* DNS Proxy is in use by another client */
+    kDNSX_Timeout                   = -65568    
+} DNSXErrorType;
+
+// A max of 5 input interfaces can be processed at one time
+#define MaxInputIf 5
+#define IfIndex uint64_t
+#define kDNSIfindexAny 0
+
+// Enable DNS Proxy with an appropriate parameter defined below
+typedef enum
+{
+    kDNSProxyEnable = 1
+    // Other values reserved for future use
+} DNSProxyParameters;
+
+/*********************************************************************************************
+*
+*  Enable DNS Proxy Functionality
+*
+*********************************************************************************************/
+
+/* DNSXEnableProxy : Turns ON the DNS Proxy (Details below)
+ *
+ * DNSXEnableProxyReply() parameters:
+ *
+ * connRef:                  The DNSXConnRef initialized by DNSXEnableProxy().
+ *
+ * errCode:                  Will be kDNSX_NoError on success, otherwise will indicate the 
+ *                           failure that occurred.  Other parameters are undefined if 
+ *                           errCode is nonzero.
+ *
+ */
+
+typedef void (*DNSXEnableProxyReply)
+(
+    DNSXConnRef           connRef,
+    DNSXErrorType         errCode 
+);
+
+/* DNSXEnableProxy
+ * 
+ * Enables the DNS Proxy functionality which will remain ON until the client terminates explictly (or exits/crashes).
+ * Client can turn it OFF by passing the returned DNSXConnRef to DNSXRefDeAlloc()
+ * 
+ * DNSXEnableProxy() Parameters:
+ *
+ * connRef:                   A pointer to DNSXConnRef that is initialized to NULL when called for the first  
+ *                            time. If the call succeeds it will be initialized to a non-NULL value.
+ *                            Client terminates the DNS Proxy by passing this DNSXConnRef to DNSXRefDeAlloc().
+ *
+ * proxyparam:                Enable DNS Proxy functionality with parameters that are described in
+ *                            DNSProxyParameters above.
+ *
+ * inIfindexArr[MaxInputIf]:  List of input interfaces from which the DNS queries will be accepted and
+ *                            forwarded to the output interface specified below. The daemon processes
+ *                            MaxInputIf entries in the list. For eg. if one has less than MaxInputIfs
+ *                            values, just initialize the other values to be 0. Note: This field needs to
+ *                            be initialized by the client.
+ *
+ * outIfindex:                Output interface on which the query will be forwarded.
+ *                            Passing kDNSIfindexAny causes DNS Queries to be sent on the primary interface.
+ *
+ * clientq:                   Queue the client wants to schedule the callBack on (Note: Must not be NULL)
+ *
+ * callBack:                  CallBack function for the client that indicates success or failure.
+ *                            Note: callback may be invoked more than once, For eg. if enabling DNS Proxy
+ *                            first succeeds and the daemon possibly crashes sometime later. 
+ *
+ * return value:              Returns kDNSX_NoError when no error otherwise returns an error code indicating
+ *                            the error that occurred. Note: A return value of kDNSX_NoError does not mean 
+ *                            that DNS Proxy was successfully enabled. The callBack may asynchronously
+ *                            return an error (such as kDNSX_DaemonNotRunning/ kDNSX_Engaged)
+ *
+ */
+
+DNSXErrorType DNSXEnableProxy
+(
+    DNSXConnRef              *connRef,
+    DNSProxyParameters       proxyparam,
+    IfIndex                  inIfindexArr[MaxInputIf],
+    IfIndex                  outIfindex,
+    dispatch_queue_t         clientq,
+    DNSXEnableProxyReply     callBack
+);
+
+/* DNSXRefDeAlloc()
+ *
+ * Terminate a connection with the daemon and free memory associated with the DNSXConnRef.
+ * Used to Disable DNS Proxy on that connection.
+ *
+ * connRef:        A DNSXConnRef initialized by any of the DNSX*() calls.
+ *
+ */
+void DNSXRefDeAlloc(DNSXConnRef connRef);
+
+#endif  /* _DNS_SERVICES_H */
diff --git a/mDNSMacOSX/Private/dns_xpc.h b/mDNSMacOSX/Private/dns_xpc.h
new file mode 100644 (file)
index 0000000..10ae01f
--- /dev/null
@@ -0,0 +1,33 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2012 Apple Inc. All rights reserved.
+ *
+ * Defines the common interface between mDNSResponder and the Private ClientLibrary(libdnsprivate.dylib)
+ * Uses XPC as the IPC Mechanism
+ *
+ */
+
+#ifndef DNS_XPC_H
+#define DNS_XPC_H
+
+#define kDNSProxyService "com.apple.mDNSResponder.dnsproxy"
+
+#define kDNSProxyParameters     "DNSProxyParameters"
+
+#define kDNSInIfindex0          "InputArrayInterfaceIndex[0]"
+#define kDNSInIfindex1          "InputArrayInterfaceIndex[1]"
+#define kDNSInIfindex2          "InputArrayInterfaceIndex[2]"
+#define kDNSInIfindex3          "InputArrayInterfaceIndex[3]"
+#define kDNSInIfindex4          "InputArrayInterfaceIndex[4]"
+
+#define kDNSOutIfindex          "OutputInterfaceIndex"
+
+#define kDNSDaemonReply         "DaemonReplyStatusToClient"
+
+typedef enum
+{
+    kDNSMsgReceived       =  0,
+    kDNSDaemonEngaged 
+} DaemonReplyStatusCodes;
+
+#endif // DNS_XPC_H
diff --git a/mDNSMacOSX/Private/xpc_services.c b/mDNSMacOSX/Private/xpc_services.c
new file mode 100644 (file)
index 0000000..7a0e29f
--- /dev/null
@@ -0,0 +1,255 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2012 Apple Inc. All rights reserved.
+ *
+ * xpc_services.c
+ * mDNSResponder 
+ *
+ * XPC as an IPC mechanism to communicate with Clients. Open only to Apple OSX/iOS clients
+ */
+
+#include "xpc_services.h"
+#include "dns_xpc.h"
+
+#ifndef UNICAST_DISABLED
+
+#include "dnsproxy.h"        // DNSProxyInit/ProxyUDPCallback/ProxyTCPCallback
+#include "mDNSMacOSX.h"      // KQueueLock/KQueueUnlock
+#include <xpc/xpc.h>
+#include <xpc/private.h>     // xpc_connection_copy_entitlement_value 
+
+// ***************************************************************************
+// Globals
+extern mDNS mDNSStorage;
+static int dps_client_pid; // To track current active client using DNS Proxy Service
+static dispatch_queue_t dps_queue = NULL;
+// ***************************************************************************
+
+// prints current XPC Server State
+mDNSexport void xpcserver_info(mDNS *const m)
+{
+
+    LogMsg("----- Active XPC Clients -----");
+    if (dps_client_pid)
+       LogMsg("DNSProxy->Client[%d]: InputIntfs[%d, %d, %d, %d, %d] Output[%d]", dps_client_pid, m->dp_ipintf[0],
+                m->dp_ipintf[1], m->dp_ipintf[2], m->dp_ipintf[3], m->dp_ipintf[4], m->dp_opintf); 
+}
+
+
+mDNSlocal void ActivateDNSProxy(mDNSu32 IpIfArr[MaxIp], mDNSu32 OpIf, mDNSBool proxy_off)
+{
+
+    LogInfo("ActivateDNSProxy: InterfaceIndex List by Client: Input[%d, %d, %d, %d, %d] Output[%d] ", IpIfArr[0], IpIfArr[1],
+             IpIfArr[2], IpIfArr[3], IpIfArr[4], OpIf);
+    KQueueLock(&mDNSStorage); 
+    DNSProxyInit(&mDNSStorage, IpIfArr, OpIf);
+    if (proxy_off) // Open skts only if proxy was OFF else we may end up opening extra skts
+        mDNSPlatformInitDNSProxySkts(&mDNSStorage, ProxyUDPCallback, ProxyTCPCallback);
+    KQueueUnlock(&mDNSStorage, "DNSProxy Activated");
+}
+
+mDNSlocal void handle_dps_terminate()
+{
+
+    LogInfo("handle_dps_terminate: Client PID[%d] terminated connection or crashed. Proceed to terminate DNSProxy", dps_client_pid);
+    // Clear the Client's PID, so that we can now accept new DPS requests
+    dps_client_pid = 0;
+
+    KQueueLock(&mDNSStorage);
+    mDNSPlatformCloseDNSProxySkts(&mDNSStorage);
+    // TBD: Close TCP Sockets
+    DNSProxyTerminate(&mDNSStorage);
+    KQueueUnlock(&mDNSStorage, "DNSProxy Deactivated");
+}
+
+mDNSlocal void handle_dps_request(xpc_object_t req)
+{
+    int dps_tmp_client;
+    mDNSBool proxy_off = mDNSfalse;
+    xpc_connection_t remote_conn = xpc_dictionary_get_remote_connection(req);
+    dps_tmp_client = (int) xpc_connection_get_pid(remote_conn);
+
+    LogInfo("handle_dps_request: Handler for DNS Proxy Requests");
+
+    if (dps_client_pid <= 0)
+    {
+        LogInfo("handle_dps_request: DNSProxy is not engaged (New Client)");
+        // No Active Client, save new Client's PID (also indicates DNS Proxy was OFF)
+        dps_client_pid = dps_tmp_client;
+        proxy_off = mDNStrue;        
+    }
+    else
+    {
+        // We already have an active DNS Proxy Client and until that client does not terminate the connection
+        // or crashes, a new client cannot change/override the current DNS Proxy settings.
+        if (dps_client_pid != dps_tmp_client)
+        {
+            LogMsg("handle_dps_request: A Client is already using DNS Proxy and your request cannot be handled at this time");
+            // Return Engaged Status to the client
+            xpc_object_t reply = xpc_dictionary_create(NULL, NULL, 0); 
+            if (reply)
+            {   
+                xpc_dictionary_set_uint64(reply, kDNSDaemonReply, kDNSDaemonEngaged);
+                xpc_connection_send_message(remote_conn, reply);
+                xpc_release(reply);  
+            }   
+            else
+            {   
+                LogMsg("handle_dps_request: Reply Dictionary could not be created");
+                return;
+            }
+            // We do not really need to terminate the connection with the client
+            // as it may try again later which is fine
+            return;   
+        }
+    }
+    
+    // Return Success Status to the client
+    xpc_object_t response = xpc_dictionary_create(NULL, NULL, 0); 
+    if (response)
+    {
+        xpc_dictionary_set_uint64(response, kDNSDaemonReply, kDNSMsgReceived);
+        xpc_connection_send_message(remote_conn, response);
+        xpc_release(response);  
+    }
+    else
+    { 
+        LogMsg("handle_dps_request: Response Dictionary could not be created");
+        return;
+    }
+
+    // Proceed to get DNS Proxy Settings from the Client
+    if (xpc_dictionary_get_uint64(req, kDNSProxyParameters))
+    {
+        mDNSu32 inIf[MaxIp], outIf;
+        
+        inIf[0]   = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSInIfindex0);
+        inIf[1]   = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSInIfindex1);
+        inIf[2]   = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSInIfindex2);
+        inIf[3]   = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSInIfindex3);
+        inIf[4]   = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSInIfindex4);
+        outIf     = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSOutIfindex);
+        
+        ActivateDNSProxy(inIf, outIf, proxy_off);
+    }
+    
+}
+
+// Verify Client's Entitlement
+mDNSlocal mDNSBool IsEntitled(xpc_connection_t conn, const char *password)
+{
+    mDNSBool entitled = mDNSfalse;
+    xpc_object_t ent = xpc_connection_copy_entitlement_value(conn, password);
+
+    if (ent)
+    {
+        if (xpc_get_type(ent) == XPC_TYPE_BOOL && xpc_bool_get_value(ent))
+        {
+            entitled = mDNStrue;
+        }
+        xpc_release(ent);
+    }
+    else
+    {
+        LogMsg("IsEntitled: Client Entitlement is NULL");
+    }
+    
+    return entitled;
+}
+
+mDNSlocal void accept_dps_client(xpc_connection_t conn)
+{
+    uid_t euid;
+    euid = xpc_connection_get_euid(conn);
+
+    if (euid != 0 || !IsEntitled(conn, kDNSProxyService))
+    {   
+        LogMsg("accept_dps_client: DNSProxyService Client Pid[%d] is missing Entitlement or is not root!", (int) xpc_connection_get_pid(conn));
+        xpc_connection_cancel(conn);
+        return; 
+    }
+
+    xpc_retain(conn);
+    xpc_connection_set_target_queue(conn, dps_queue);
+    xpc_connection_set_event_handler(conn, ^(xpc_object_t req_msg)
+        {
+            xpc_type_t type = xpc_get_type(req_msg);
+
+            if (type == XPC_TYPE_DICTIONARY)
+            {
+                handle_dps_request(req_msg);
+            }
+            // We hit the case below only if Client Terminated DPS Connection OR Crashed
+            else
+            {
+                LogInfo("accept_dps_client: DPS Client %p teared down the connection or Crashed", (void *) conn);
+                // Only the Client that has activated DPS should be able to terminate it
+                if (((int)xpc_connection_get_pid(conn)) == dps_client_pid)
+                    handle_dps_terminate(); 
+                xpc_release(conn);
+            }
+        });
+    xpc_connection_resume(conn);
+                
+}
+
+mDNSlocal void init_dnsproxy_service(void)
+{
+    
+    xpc_connection_t dps_listener = xpc_connection_create_mach_service(kDNSProxyService, NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER);
+    if (!dps_listener || xpc_get_type(dps_listener) != XPC_TYPE_CONNECTION)
+    {
+        LogMsg("init_dnsproxy_service: Error Creating XPC Listener for DNSProxyService !!");
+        return;
+    }
+    
+    dps_queue = dispatch_queue_create("com.apple.mDNSResponder.dnsproxyservice_queue", NULL);
+
+    xpc_connection_set_event_handler(dps_listener, ^(xpc_object_t eventmsg)
+        {
+            xpc_type_t type = xpc_get_type(eventmsg);
+
+            if (type == XPC_TYPE_CONNECTION)
+            {
+                LogInfo("init_dnsproxy_service: New DNSProxyService Client %p", eventmsg);
+                accept_dps_client(eventmsg);
+            }
+            // Ideally, we would never hit the cases below
+            else if (type == XPC_TYPE_ERROR)
+            {
+                LogMsg("init_dnsproxy_service: XPCError: %s", xpc_dictionary_get_string(eventmsg, XPC_ERROR_KEY_DESCRIPTION));
+                return;
+            }
+            else
+            {
+                LogMsg("init_dnsproxy_service: Unknown EventMsg type");
+                return;
+            }
+        });
+    xpc_connection_resume(dps_listener);
+
+}
+
+mDNSexport void xpc_server_init()
+{
+    // Add XPC Services here
+    init_dnsproxy_service();
+}
+
+#else // !UNICAST_DISABLED
+
+mDNSexport void xpc_server_init()
+{
+    return;
+}
+
+mDNSexport void xpcserver_info(mDNS *const m)
+{
+    (void) m;
+    
+    return;
+}
+
+#endif // !UNICAST_DISABLED
+
diff --git a/mDNSMacOSX/Private/xpc_services.h b/mDNSMacOSX/Private/xpc_services.h
new file mode 100644 (file)
index 0000000..50081be
--- /dev/null
@@ -0,0 +1,21 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2012 Apple Inc. All rights reserved.
+ *
+ *
+ *
+ *    File:       xpc_services.h
+ *
+ *    Contains:   Interfaces necessary to talk to xpc_services.c
+ *
+ */
+
+#ifndef XPC_SERVICES_H
+#define XPC_SERVICES_H
+
+#include "mDNSEmbeddedAPI.h"
+
+extern void xpc_server_init(void);
+extern void xpcserver_info(mDNS *const m);
+
+#endif // XPC_SERVICES_H
index 30de198ebf412726177b3927be32d11b6a79d960..130e327601f9adfbbdce2a29f5a57730a70222d0 100644 (file)
@@ -9,22 +9,16 @@ needed and handles requests from mDNSResponder.
   10 seconds of idle time.
 
 * The com.apple.mDNSResponder LaunchD job specifies the account under
   10 seconds of idle time.
 
 * The com.apple.mDNSResponder LaunchD job specifies the account under
-  which to run, so that mDNSResponder starts as _mdnsresponder.  When
-  run as root--- e.g. from the command line with `sudo'---
-  mDNSResponder drops privileges itself.
+  which to run, so that mDNSResponder starts as _mdnsresponder.
 
 * A subdirectory named "mdns" and owned by _mdnsresponder has been
   created in /var/run.  The PID file and uDNS server socket has been
   moved to that subdirectory.
 
 * There are currently six remote procedure calls handled by
 
 * A subdirectory named "mdns" and owned by _mdnsresponder has been
   created in /var/run.  The PID file and uDNS server socket has been
   moved to that subdirectory.
 
 * There are currently six remote procedure calls handled by
-  mDNSResponderHelper: mDNSDynamicStoreSetConfig,
-  mDNSPreferencesSetName, mDNSKeychainGetSecrets,
+  mDNSResponderHelper: mDNSPreferencesSetName, mDNSKeychainGetSecrets,
   mDNSConfigureServer, and mDNSAutoTunnelSetKeys
 
   mDNSConfigureServer, and mDNSAutoTunnelSetKeys
 
-* mDNSDynamicStoreSetConfig allows mDNSResponder to set the
-  MulticastDNS, PrivateDNS, or DynamicDNS configurations.
-
 * mDNSPreferencesSetName allows mDNSResponder to set the computer name
   or local host name, and displays a notification if there was a
   conflict.
 * mDNSPreferencesSetName allows mDNSResponder to set the computer name
   or local host name, and displays a notification if there was a
   conflict.
diff --git a/mDNSMacOSX/VPNService.c b/mDNSMacOSX/VPNService.c
new file mode 100644 (file)
index 0000000..8c1bf1d
--- /dev/null
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2013 Apple Computer, 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 "mDNSMacOSX.h"
+#include <SystemConfiguration/VPNAppLayerPrivate.h>
+
+mDNSexport mDNSs32 mDNSPlatformGetServiceID(mDNS *const m, DNSQuestion *q)
+{
+    (void) m;
+    int sid;
+
+    if (q->pid)
+    {
+        sid = VPNAppLayerGetMatchingServiceIdentifier(q->pid, NULL);
+    }
+    else
+    {
+        sid = VPNAppLayerGetMatchingServiceIdentifier(0, q->uuid);
+    }
+    LogInfo("mDNSPlatformGetServiceID: returning %d for %##s (%s)", sid, q->qname.c, DNSTypeName(q->qtype));
+    return sid;
+}
diff --git a/mDNSMacOSX/com.apple.networking.mDNSResponder b/mDNSMacOSX/com.apple.networking.mDNSResponder
new file mode 100644 (file)
index 0000000..d07c99d
--- /dev/null
@@ -0,0 +1 @@
+? [= LoggerID com.apple.networking.mDNSResponder] file /Library/Logs/CrashReporter/com.apple.networking.mDNSResponder.log rotate file_max=1M compress
index 151053fa833b8ca8db40a98ae03641703513badd..3b3ed393d0377e0538169326918551c2beb5f1fd 100644 (file)
  *
  */
 
  *
  */
 
-// We set VERSION_MIN_REQUIRED to 10.4 to avoid "bootstrap_register is deprecated" warnings from bootstrap.h
-#define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_4
-
 #include <mach/mach.h>
 #include <mach/mach_error.h>
 #include <mach/mach.h>
 #include <mach/mach_error.h>
-#include <servers/bootstrap.h>
 #include <sys/types.h>
 #include <errno.h>
 #include <signal.h>
 #include <sys/types.h>
 #include <errno.h>
 #include <signal.h>
 #include <paths.h>
 #include <fcntl.h>
 #include <launch.h>
 #include <paths.h>
 #include <fcntl.h>
 #include <launch.h>
+#include <launch_priv.h>         // for launch_socket_service_check_in()
 #include <vproc.h>
 #include <pwd.h>
 #include <sys/event.h>
 #include <pthread.h>
 #include <sandbox.h>
 #include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>
 #include <vproc.h>
 #include <pwd.h>
 #include <sys/event.h>
 #include <pthread.h>
 #include <sandbox.h>
 #include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>
-
-#if TARGET_OS_EMBEDDED
-#include <bootstrap_priv.h>
-
-#define bootstrap_register(A,B,C) bootstrap_register2((A),(B),(C),0)
-#endif
+#include <asl.h>
+#include <syslog.h>
+#include <err.h>
+#include <sysexits.h>
+#include <bootstrap_priv.h>      // for bootstrap_check_in() 
 
 #include "DNSServiceDiscoveryRequestServer.h"
 #include "DNSServiceDiscoveryReply.h"
 
 #include "DNSServiceDiscoveryRequestServer.h"
 #include "DNSServiceDiscoveryReply.h"
 #include "mDNSMacOSX.h"             // Defines the specific types needed to run mDNS on this platform
 
 #include "uds_daemon.h"             // Interface to the server side implementation of dns_sd.h
 #include "mDNSMacOSX.h"             // Defines the specific types needed to run mDNS on this platform
 
 #include "uds_daemon.h"             // Interface to the server side implementation of dns_sd.h
+#include "xpc_services.h"           // Interface to XPC services
 
 
-#include <DNSServiceDiscovery/DNSServiceDiscovery.h>
+#include "../mDNSMacOSX/DNSServiceDiscovery.h"
 #include "helper.h"
 
 #include "helper.h"
 
+static aslclient log_client = NULL;
+static aslmsg log_msg = NULL;
+
+// Used on Embedded Side for Reading mDNSResponder Managed Preferences Profile
+#if TARGET_OS_EMBEDDED
+#define kmDNSEnableLoggingStr CFSTR("EnableLogging")
+#define kmDNSResponderPrefIDStr "com.apple.mDNSResponder.plist"
+#define kmDNSResponderPrefID CFSTR(kmDNSResponderPrefIDStr)
+#endif
+
 //*************************************************************************************************************
 #if COMPILER_LIKES_PRAGMA_MARK
 #pragma mark - Globals
 //*************************************************************************************************************
 #if COMPILER_LIKES_PRAGMA_MARK
 #pragma mark - Globals
@@ -68,7 +75,6 @@ static mDNS_PlatformSupport PlatformStorage;
 #define RR_CACHE_SIZE ((16*1024) / sizeof(CacheRecord))
 static CacheEntity rrcachestorage[RR_CACHE_SIZE];
 
 #define RR_CACHE_SIZE ((16*1024) / sizeof(CacheRecord))
 static CacheEntity rrcachestorage[RR_CACHE_SIZE];
 
-static const char kmDNSBootstrapName[] = "com.apple.mDNSResponderRestart";
 static mach_port_t m_port            = MACH_PORT_NULL;
 
 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 static mach_port_t m_port            = MACH_PORT_NULL;
 
 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
@@ -78,8 +84,6 @@ static mach_port_t client_death_port = MACH_PORT_NULL;
 static mach_port_t signal_port       = MACH_PORT_NULL;
 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 
 static mach_port_t signal_port       = MACH_PORT_NULL;
 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 
-static mach_port_t server_priv_port  = MACH_PORT_NULL;
-
 static dnssd_sock_t *launchd_fds = mDNSNULL;
 static mDNSu32 launchd_fds_count = 0;
 
 static dnssd_sock_t *launchd_fds = mDNSNULL;
 static mDNSu32 launchd_fds_count = 0;
 
@@ -91,8 +95,6 @@ static mDNSu32 launchd_fds_count = 0;
 // even extra-slow clients a fair chance before we cut them off.
 #define MDNS_MM_TIMEOUT 250
 
 // even extra-slow clients a fair chance before we cut them off.
 #define MDNS_MM_TIMEOUT 250
 
-static int restarting_via_mach_init = 0;    // Used on Jaguar/Panther when daemon is started via mach_init mechanism
-static int started_via_launchdaemon = 0;    // Indicates we're running on Tiger or later, where daemon is managed by launchd
 static mDNSBool advertise = mDNS_Init_AdvertiseLocalAddresses; // By default, advertise addresses (& other records) via multicast
 
 extern mDNSBool StrictUnicastOrdering;
 static mDNSBool advertise = mDNS_Init_AdvertiseLocalAddresses; // By default, advertise addresses (& other records) via multicast
 
 extern mDNSBool StrictUnicastOrdering;
@@ -710,7 +712,7 @@ mDNSlocal mStatus AddDomainToBrowser(DNSServiceBrowser *browser, const domainnam
     AssignDomainName(&question->domain, d);
     question->next = browser->qlist;
     LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", browser->ClientMachPort, browser->type.c, d->c);
     AssignDomainName(&question->domain, d);
     question->next = browser->qlist;
     LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", browser->ClientMachPort, browser->type.c, d->c);
-    err = mDNS_StartBrowse(&mDNSStorage, &question->q, &browser->type, d, mDNSInterface_Any, 0, mDNSfalse, mDNSfalse, FoundInstance, browser);
+    err = mDNS_StartBrowse(&mDNSStorage, &question->q, &browser->type, d, mDNSNULL, mDNSInterface_Any, 0, mDNSfalse, mDNSfalse, FoundInstance, browser);
     if (!err)
         browser->qlist = question;
     else
     if (!err)
         browser->qlist = question;
     else
@@ -768,7 +770,7 @@ mDNSexport kern_return_t provide_DNSServiceBrowserCreate_rpc(mach_port_t unuseds
     // Check other parameters
     domainname t, d;
     t.c[0] = 0;
     // Check other parameters
     domainname t, d;
     t.c[0] = 0;
-    mDNSs32 NumSubTypes = ChopSubTypes(regtype);    // Note: Modifies regtype string to remove trailing subtypes
+    mDNSs32 NumSubTypes = ChopSubTypes(regtype, mDNSNULL);    // Note: Modifies regtype string to remove trailing subtypes
     if (NumSubTypes < 0 || NumSubTypes > 1)               { errormsg = "Bad Service SubType"; goto badparam; }
     if (NumSubTypes == 1 && !AppendDNSNameString(&t, regtype + strlen(regtype) + 1))
     { errormsg = "Bad Service SubType"; goto badparam; }
     if (NumSubTypes < 0 || NumSubTypes > 1)               { errormsg = "Bad Service SubType"; goto badparam; }
     if (NumSubTypes == 1 && !AppendDNSNameString(&t, regtype + strlen(regtype) + 1))
     { errormsg = "Bad Service SubType"; goto badparam; }
@@ -1052,7 +1054,7 @@ mDNSlocal mStatus AddServiceInstance(DNSServiceRegistration *x, const domainname
         { LogMsg("Requested addition of domain %##s already in list", domain->c); return mStatus_AlreadyRegistered; }
     }
 
         { LogMsg("Requested addition of domain %##s already in list", domain->c); return mStatus_AlreadyRegistered; }
     }
 
-    SubTypes = AllocateSubTypes(x->NumSubTypes, x->regtype);
+    SubTypes = AllocateSubTypes(x->NumSubTypes, x->regtype, mDNSNULL);
     if (x->NumSubTypes && !SubTypes) return mStatus_NoMemoryErr;
 
     si = mallocL("ServiceInstance", sizeof(*si) - sizeof(RDataBody) + x->rdsize);
     if (x->NumSubTypes && !SubTypes) return mStatus_NoMemoryErr;
 
     si = mallocL("ServiceInstance", sizeof(*si) - sizeof(RDataBody) + x->rdsize);
@@ -1063,6 +1065,7 @@ mDNSlocal mStatus AddServiceInstance(DNSServiceRegistration *x, const domainname
     si->autoname        = x->autoname;
     si->name            = x->autoname ? mDNSStorage.nicelabel : x->name;
     si->domain          = *domain;
     si->autoname        = x->autoname;
     si->name            = x->autoname ? mDNSStorage.nicelabel : x->name;
     si->domain          = *domain;
+    si->srs.AnonData    = mDNSNULL;    
 
     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, 0);
 
     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, 0);
@@ -1129,7 +1132,7 @@ mDNSexport kern_return_t provide_DNSServiceRegistrationCreate_rpc(mach_port_t un
     // Check for sub-types after the service type
     size_t reglen = strlen(regtype) + 1;
     if (reglen > MAX_ESCAPED_DOMAIN_NAME) { errormsg = "reglen too long"; goto badparam; }
     // Check for sub-types after the service type
     size_t reglen = strlen(regtype) + 1;
     if (reglen > MAX_ESCAPED_DOMAIN_NAME) { errormsg = "reglen too long"; goto badparam; }
-    mDNSs32 NumSubTypes = ChopSubTypes(regtype);    // Note: Modifies regtype string to remove trailing subtypes
+    mDNSs32 NumSubTypes = ChopSubTypes(regtype, mDNSNULL);    // Note: Modifies regtype string to remove trailing subtypes
     if (NumSubTypes < 0) { errormsg = "Bad Service SubType"; goto badparam; }
 
     // Check other parameters
     if (NumSubTypes < 0) { errormsg = "Bad Service SubType"; goto badparam; }
 
     // Check other parameters
@@ -1560,6 +1563,30 @@ fail:
 #pragma mark - Startup, shutdown, and supporting code
 #endif
 
 #pragma mark - Startup, shutdown, and supporting code
 #endif
 
+mDNSlocal void ExitCallback(int sig)
+{
+    (void)sig; // Unused
+    LogMsg("%s stopping", mDNSResponderVersionString);
+
+    debugf("ExitCallback: Aborting MIG clients");
+    while (DNSServiceDomainEnumerationList)
+        AbortClient(DNSServiceDomainEnumerationList->ClientMachPort, DNSServiceDomainEnumerationList);
+    while (DNSServiceBrowserList)
+        AbortClient(DNSServiceBrowserList->ClientMachPort, DNSServiceBrowserList);
+    while (DNSServiceResolverList)
+        AbortClient(DNSServiceResolverList->ClientMachPort, DNSServiceResolverList);
+    while (DNSServiceRegistrationList)
+        AbortClient(DNSServiceRegistrationList->ClientMachPort, DNSServiceRegistrationList);
+
+    if (udsserver_exit() < 0) 
+        LogMsg("ExitCallback: udsserver_exit failed");
+
+    debugf("ExitCallback: mDNS_StartExit");
+    mDNS_StartExit(&mDNSStorage);
+}
+
+#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
+
 mDNSlocal void DNSserverCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
 {
     mig_reply_error_t *request = msg;
 mDNSlocal void DNSserverCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
 {
     mig_reply_error_t *request = msg;
@@ -1653,76 +1680,6 @@ done:
     KQueueUnlock(&mDNSStorage, "Mach client event");
 }
 
     KQueueUnlock(&mDNSStorage, "Mach client event");
 }
 
-mDNSlocal kern_return_t registerBootstrapService()
-{
-    kern_return_t status;
-    mach_port_t service_rcv_port;
-
-    LogMsg("Registering Bootstrap Service");
-
-    /*
-     * See if our service name is already registered and if we have privilege to check in.
-     */
-    status = bootstrap_check_in(bootstrap_port, (char*)kmDNSBootstrapName, &service_rcv_port);
-    if (status == KERN_SUCCESS)
-    {
-        /*
-         * If so, we must be a followup instance of an already defined server.  In that case,
-         * the bootstrap port we inherited from our parent is the server's privilege port, so set
-         * that in case we have to unregister later (which requires the privilege port).
-         */
-        server_priv_port = bootstrap_port;
-        restarting_via_mach_init = TRUE;
-    }
-    else
-    {
-        LogMsg("registerBootstrapService: ERROR!! Registering Bootstrap Service failed %d", status);
-        return status;
-    }
-
-    /*
-     * We have no intention of responding to requests on the service port.  We are not otherwise
-     * a Mach port-based service.  We are just using this mechanism for relaunch facilities.
-     * So, we can dispose of all the rights we have for the service port.  We don't destroy the
-     * send right for the server's privileged bootstrap port - in case we have to unregister later.
-     */
-    mach_port_destroy(mach_task_self(), service_rcv_port);
-    return status;
-}
-
-mDNSlocal kern_return_t destroyBootstrapService()
-{
-    debugf("Destroying Bootstrap Service");
-    return bootstrap_register(server_priv_port, (char*)kmDNSBootstrapName, MACH_PORT_NULL);
-}
-
-mDNSlocal void ExitCallback(int sig)
-{
-    (void)sig; // Unused
-    LogMsg("%s stopping", mDNSResponderVersionString);
-
-    debugf("ExitCallback");
-    if (!mDNS_DebugMode && !started_via_launchdaemon)
-        destroyBootstrapService();
-
-    debugf("ExitCallback: Aborting MIG clients");
-    while (DNSServiceDomainEnumerationList)
-        AbortClient(DNSServiceDomainEnumerationList->ClientMachPort, DNSServiceDomainEnumerationList);
-    while (DNSServiceBrowserList)
-        AbortClient(DNSServiceBrowserList->ClientMachPort, DNSServiceBrowserList);
-    while (DNSServiceResolverList)
-        AbortClient(DNSServiceResolverList->ClientMachPort, DNSServiceResolverList);
-    while (DNSServiceRegistrationList)
-        AbortClient(DNSServiceRegistrationList->ClientMachPort, DNSServiceRegistrationList);
-
-    if (udsserver_exit() < 0) LogMsg("ExitCallback: udsserver_exit failed");
-
-    debugf("ExitCallback: mDNS_StartExit");
-    mDNS_StartExit(&mDNSStorage);
-}
-
-#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)
 {
 // Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit
 mDNSlocal void HandleSIG(int sig)
 {
@@ -1751,43 +1708,19 @@ mDNSlocal void HandleSIG(int sig)
 mDNSlocal void INFOCallback(void)
 {
     mDNSs32 utc = mDNSPlatformUTC();
 mDNSlocal void INFOCallback(void)
 {
     mDNSs32 utc = mDNSPlatformUTC();
-    DNSServiceDomainEnumeration *e;
-    DNSServiceBrowser           *b;
-    DNSServiceResolver          *l;
-    DNSServiceRegistration      *r;
     NetworkInterfaceInfoOSX     *i;
     DNSServer *s;
     McastResolver *mr;
 
     NetworkInterfaceInfoOSX     *i;
     DNSServer *s;
     McastResolver *mr;
 
+    // Create LoggerID(Key)->com.apple.networking.mDNSResponder(Value) pair when SIGINFO is received.
+    // This key-value pair is used as a condition by syslogd to Log to com.apple.networking.mDNSResponder.log file
+    // present in /etc/asl/com.apple.networking.mDNSResponder.
+    asl_set(log_msg, "LoggerID", "com.apple.networking.mDNSResponder");
+
     LogMsg("---- BEGIN STATE LOG ---- %s %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers);
 
     udsserver_info(&mDNSStorage);
     LogMsg("---- BEGIN STATE LOG ---- %s %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers);
 
     udsserver_info(&mDNSStorage);
-
-    LogMsgNoIdent("--------- Mach Clients ---------");
-    if (!DNSServiceDomainEnumerationList && !DNSServiceBrowserList && !DNSServiceResolverList && !DNSServiceRegistrationList)
-        LogMsgNoIdent("<None>");
-    else
-    {
-        for (e = DNSServiceDomainEnumerationList; e; e=e->next)
-            LogMsgNoIdent("%5d: Mach DomainEnumeration   %##s", e->ClientMachPort, e->dom.qname.c);
-
-        for (b = DNSServiceBrowserList; b; b=b->next)
-        {
-            DNSServiceBrowserQuestion *qptr;
-            for (qptr = b->qlist; qptr; qptr = qptr->next)
-                LogMsgNoIdent("%5d: Mach ServiceBrowse       %##s", b->ClientMachPort, qptr->q.qname.c);
-        }
-
-        for (l = DNSServiceResolverList; l; l=l->next)
-            LogMsgNoIdent("%5d: Mach ServiceResolve      %##s", l->ClientMachPort, l->i.name.c);
-
-        for (r = DNSServiceRegistrationList; r; r=r->next)
-        {
-            ServiceInstance *si;
-            for (si = r->regs; si; si = si->next)
-                LogMsgNoIdent("%5d: Mach ServiceInstance     %##s %u", si->ClientMachPort, si->srs.RR_SRV.resrec.name->c, mDNSVal16(si->srs.RR_SRV.resrec.rdata->u.srv.port));
-        }
-    }
+    xpcserver_info(&mDNSStorage);
 
     LogMsgNoIdent("----- KQSocketEventSources -----");
     if (!gEventSources) LogMsgNoIdent("<None>");
 
     LogMsgNoIdent("----- KQSocketEventSources -----");
     if (!gEventSources) LogMsgNoIdent("<None>");
@@ -1795,10 +1728,7 @@ mDNSlocal void INFOCallback(void)
     {
         KQSocketEventSource *k;
         for (k = gEventSources; k; k=k->next)
     {
         KQSocketEventSource *k;
         for (k = gEventSources; k; k=k->next)
-        {
-            LogMsgNoIdent("%3d %s", k->fd, k->kqs.KQtask);
-            usleep((mDNSStorage.KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
-        }
+            LogMsgNoIdent("%3d %s %s", k->fd, k->kqs.KQtask, k->fd == mDNSStorage.uds_listener_skt ? "Listener for incoming UDS clients" : " ");
     }
 
     LogMsgNoIdent("------ Network Interfaces ------");
     }
 
     LogMsgNoIdent("------ Network Interfaces ------");
@@ -1824,9 +1754,9 @@ mDNSlocal void INFOCallback(void)
                               i->ifinfo.IPv4Available ? "v4" : "  ",
                               i->ifinfo.IPv4Available ? (mDNSv4Addr*)&i->ifa_v4addr : &zerov4Addr,
                               i->ifinfo.IPv6Available ? "v6" : "  ",
                               i->ifinfo.IPv4Available ? "v4" : "  ",
                               i->ifinfo.IPv4Available ? (mDNSv4Addr*)&i->ifa_v4addr : &zerov4Addr,
                               i->ifinfo.IPv6Available ? "v6" : "  ",
-                              i->ifinfo.Advertise ? "" : " ",
-                              i->ifinfo.McastTxRx ? "" : " ",
-                              !(i->ifinfo.InterfaceActive && i->ifinfo.NetWake) ? " " : !sps[0] ? "☼" : "☀",
+                              i->ifinfo.Advertise ? "A" : " ",
+                              i->ifinfo.McastTxRx ? "M" : " ",
+                              !(i->ifinfo.InterfaceActive && i->ifinfo.NetWake) ? " " : !sps[0] ? "p" : "P",
                               &i->ifinfo.ip);
 
                 if (sps[0]) LogMsgNoIdent("  %13d %#s", SPSMetric(sps[0]->resrec.rdata->u.name.c), sps[0]->resrec.rdata->u.name.c);
                               &i->ifinfo.ip);
 
                 if (sps[0]) LogMsgNoIdent("  %13d %#s", SPSMetric(sps[0]->resrec.rdata->u.name.c), sps[0]->resrec.rdata->u.name.c);
@@ -1836,23 +1766,31 @@ mDNSlocal void INFOCallback(void)
         }
     }
 
         }
     }
 
-    LogMsgNoIdent("--------- DNS Servers ----------");
+    LogMsgNoIdent("--------- DNS Servers(%d) ----------", NumUnicastDNSServers);
     if (!mDNSStorage.DNSServers) LogMsgNoIdent("<None>");
     else
     {
         for (s = mDNSStorage.DNSServers; s; s = s->next)
         {
             NetworkInterfaceInfoOSX *ifx = IfindexToInterfaceInfoOSX(&mDNSStorage, s->interface);
     if (!mDNSStorage.DNSServers) LogMsgNoIdent("<None>");
     else
     {
         for (s = mDNSStorage.DNSServers; s; s = s->next)
         {
             NetworkInterfaceInfoOSX *ifx = IfindexToInterfaceInfoOSX(&mDNSStorage, s->interface);
-            LogMsgNoIdent("DNS Server %##s %s%s%#a:%d %d %s %d %d %s",
+            LogMsgNoIdent("DNS Server %##s %s%s%#a:%d %d %s %d %d %s %s %s %s %s",
                           s->domain.c, ifx ? ifx->ifinfo.ifname : "", ifx ? " " : "", &s->addr, mDNSVal16(s->port),
                           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->penaltyTime ? s->penaltyTime - mDNS_TimeNow(&mDNSStorage) : 0, DNSScopeToString(s->scoped),
                           s->timeout, s->resGroupID,
                           s->teststate == DNSServer_Untested ? "(Untested)" :
                           s->teststate == DNSServer_Passed   ? ""           :
                           s->teststate == DNSServer_Failed   ? "(Failed)"   :
                           s->timeout, s->resGroupID,
                           s->teststate == DNSServer_Untested ? "(Untested)" :
                           s->teststate == DNSServer_Passed   ? ""           :
                           s->teststate == DNSServer_Failed   ? "(Failed)"   :
-                          s->teststate == DNSServer_Disabled ? "(Disabled)" : "(Unknown state)");
+                          s->teststate == DNSServer_Disabled ? "(Disabled)" : "(Unknown state)",
+                          s->req_A ? "v4" : "!v4",  
+                          s->req_AAAA ? "v6" : "!v6",
+                          s->cellIntf ? "cell" : "!cell",
+                          s->DNSSECAware ? "DNSSECAware" : "!DNSSECAware");
         }
     }
         }
     }
+    mDNSs32 now = mDNS_TimeNow(&mDNSStorage);
+    LogMsgNoIdent("v4answers %d", mDNSStorage.p->v4answers);
+    LogMsgNoIdent("v6answers %d", mDNSStorage.p->v6answers);
+    LogMsgNoIdent("Last DNS Trigger: %d ms ago", (now - mDNSStorage.p->DNSTrigger));
 
     LogMsgNoIdent("--------- Mcast Resolvers ----------");
     if (!mDNSStorage.McastResolvers) LogMsgNoIdent("<None>");
 
     LogMsgNoIdent("--------- Mcast Resolvers ----------");
     if (!mDNSStorage.McastResolvers) LogMsgNoIdent("<None>");
@@ -1862,12 +1800,155 @@ mDNSlocal void INFOCallback(void)
             LogMsgNoIdent("Mcast Resolver %##s timeout %u", mr->domain.c, mr->timeout);
     }
 
             LogMsgNoIdent("Mcast Resolver %##s timeout %u", mr->domain.c, mr->timeout);
     }
 
-    mDNSs32 now = mDNS_TimeNow(&mDNSStorage);
     LogMsgNoIdent("Timenow 0x%08lX (%d)", (mDNSu32)now, now);
     LogMsgNoIdent("Timenow 0x%08lX (%d)", (mDNSu32)now, now);
-
     LogMsg("----  END STATE LOG  ---- %s %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers);
     LogMsg("----  END STATE LOG  ---- %s %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers);
+    
+    // If logging is disabled, only then clear the key we set at the top of this func
+    if (!mDNS_LoggingEnabled)
+        asl_unset(log_msg, "LoggerID");
 }
 
 }
 
+mDNSlocal void DebugSetFilter()
+{
+    if (!log_client)
+        return;
+
+    // When USR1 is turned on, we log only the LOG_WARNING and LOG_NOTICE messages by default.
+    // The user has to manually do "syslog -c mDNSResponder -i" to get the LOG_INFO messages
+    // also to be logged. Most of the times, we need the INFO level messages for debugging.
+    // Hence, we set the filter to INFO level when USR1 logging is turned on to avoid 
+    // having the user to do this extra step manually. 
+
+    if (mDNS_LoggingEnabled)
+    {
+        asl_set_filter(log_client, ASL_FILTER_MASK_UPTO(ASL_LEVEL_INFO));
+        asl_set(log_msg, "LoggerID", "com.apple.networking.mDNSResponder");
+        // Create LoggerID(Key)->com.apple.networking.mDNSResponder(Value) pair when USR1 Logging is Enabled.
+        // This key-value pair is used as a condition by syslogd to Log to com.apple.networking.mDNSResponder.log file
+        // present in /etc/asl/com.apple.networking.mDNSResponder.
+    }
+    else
+    {
+        asl_set_filter(log_client, ASL_FILTER_MASK_UPTO(ASL_LEVEL_ERR));
+        asl_unset(log_msg, "LoggerID");
+        // Clear the key-value pair when USR1 Logging is Disabled, as we do not want to log to 
+        // com.apple.networking.mDNSResponder.log file in this case.
+    }
+}
+
+mDNSexport void mDNSPlatformLogToFile(int log_level, const char *buffer)
+{
+    int asl_level = ASL_LEVEL_ERR;
+
+    if (!log_client)
+    {
+        syslog(log_level, "%s", buffer);
+        return;
+    }
+    switch (log_level)
+    {
+        case LOG_ERR:
+            asl_level = ASL_LEVEL_ERR;
+            break;
+        case LOG_WARNING:
+            asl_level = ASL_LEVEL_WARNING;
+            break;
+        case LOG_NOTICE:
+            asl_level = ASL_LEVEL_NOTICE;
+            break;
+        case LOG_INFO:
+            asl_level = ASL_LEVEL_INFO;
+            break;
+        case LOG_DEBUG:
+            asl_level = ASL_LEVEL_DEBUG;
+            break;
+        default:
+            break;
+    }
+    asl_log(log_client, log_msg, asl_level, "%s", buffer);
+}
+
+// Writes the state out to the dynamic store and also affects the ASL filter level
+mDNSexport void UpdateDebugState()
+{
+    mDNSu32 one  = 1;
+    mDNSu32 zero = 0;
+
+    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+    if (!dict)
+    {
+        LogMsg("UpdateDebugState: Could not create dict");
+        return;
+    }
+
+    CFNumberRef numOne = CFNumberCreate(NULL, kCFNumberSInt32Type, &one);
+    if (!numOne)
+    {
+        LogMsg("UpdateDebugState: Could not create CFNumber one");
+        return;
+    }
+    CFNumberRef numZero = CFNumberCreate(NULL, kCFNumberSInt32Type, &zero);
+    if (!numZero)
+    {
+        LogMsg("UpdateDebugState: Could not create CFNumber zero");
+        CFRelease(numOne);
+        return;
+    }
+
+    if (mDNS_LoggingEnabled)
+        CFDictionarySetValue(dict, CFSTR("VerboseLogging"), numOne);
+    else
+        CFDictionarySetValue(dict, CFSTR("VerboseLogging"), numZero);
+
+    if (mDNS_PacketLoggingEnabled)
+        CFDictionarySetValue(dict, CFSTR("PacketLogging"), numOne);
+    else
+        CFDictionarySetValue(dict, CFSTR("PacketLogging"), numZero);
+
+    if (mDNS_McastLoggingEnabled)
+        CFDictionarySetValue(dict, CFSTR("McastLogging"), numOne);
+    else
+        CFDictionarySetValue(dict, CFSTR("McastLogging"), numZero);
+
+    if (mDNS_McastTracingEnabled)
+        CFDictionarySetValue(dict, CFSTR("McastTracing"), numOne);
+    else 
+        CFDictionarySetValue(dict, CFSTR("McastTracing"), numZero);
+
+    CFRelease(numOne);
+    CFRelease(numZero);
+    mDNSDynamicStoreSetConfig(kmDNSDebugState, mDNSNULL, dict);
+    CFRelease(dict);
+    // If we turned off USR1 logging, we need to reset the filter
+    DebugSetFilter();
+}
+
+#if TARGET_OS_EMBEDDED
+mDNSlocal void Prefschanged()
+{
+    mDNSBool mDNSProf_installed;
+    LogMsg("Prefschanged: mDNSResponder Managed Preferences have changed");
+    mDNSProf_installed = GetmDNSManagedPref(kmDNSEnableLoggingStr);
+    dispatch_async(dispatch_get_main_queue(), 
+        ^{ 
+            if (mDNSProf_installed)
+            {
+                mDNS_LoggingEnabled = mDNS_PacketLoggingEnabled = 1;
+            }
+            else
+            {
+                LogMsg("Prefschanged: mDNSDebugProfile is uninstalled -> Turning OFF USR1/USR2 Logging with SIGINFO o/p");
+                INFOCallback();
+                mDNS_LoggingEnabled = mDNS_PacketLoggingEnabled = 0;
+            }
+            UpdateDebugState();
+            // If Logging Enabled: Start Logging to com.apple.networking.mDNSResponder.log (has to be LogInfo)
+            LogInfo("Prefschanged: mDNSDebugProfile is installed -> Turned ON USR1/USR2 Logging");
+         });
+    return;
+}
+#endif //TARGET_OS_EMBEDDED
+
 #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 
 mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
 #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 
 mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
@@ -1882,6 +1963,10 @@ mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void
     KQueueLock(m);
     switch(msg_header->msgh_id)
     {
     KQueueLock(m);
     switch(msg_header->msgh_id)
     {
+    case SIGURG:
+        m->mDNSOppCaching = m->mDNSOppCaching ? mDNSfalse : mDNStrue;
+        LogMsg("SIGURG: Opportunistic Caching %s", m->mDNSOppCaching ? "Enabled" : "Disabled");
+        // FALL THROUGH to purge the cache so that we re-do the caching based on the new setting
     case SIGHUP:    {
         mDNSu32 slot;
         CacheGroup *cg;
     case SIGHUP:    {
         mDNSu32 slot;
         CacheGroup *cg;
@@ -1902,13 +1987,26 @@ mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void
     case SIGUSR1:   mDNS_LoggingEnabled = mDNS_LoggingEnabled ? 0 : 1;
         LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled ? "Enabled" : "Disabled");
         WatchDogReportingThreshold = mDNS_LoggingEnabled ? 50 : 250;
     case SIGUSR1:   mDNS_LoggingEnabled = mDNS_LoggingEnabled ? 0 : 1;
         LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled ? "Enabled" : "Disabled");
         WatchDogReportingThreshold = mDNS_LoggingEnabled ? 50 : 250;
+        UpdateDebugState();
+        // If Logging Enabled: Start Logging to com.apple.networking.mDNSResponder.log 
+        LogInfo("USR1 Logging Enabled: Start Logging to mDNSResponder Log file"); 
         break;
     case SIGUSR2:   mDNS_PacketLoggingEnabled = mDNS_PacketLoggingEnabled ? 0 : 1;
         LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled");
         break;
     case SIGUSR2:   mDNS_PacketLoggingEnabled = mDNS_PacketLoggingEnabled ? 0 : 1;
         LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled");
+        mDNS_McastTracingEnabled = (mDNS_PacketLoggingEnabled && mDNS_McastLoggingEnabled) ? mDNStrue : mDNSfalse;
+        LogInfo("SIGUSR2: Multicast Tracing is %s", mDNS_McastTracingEnabled ? "Enabled" : "Disabled");
+        UpdateDebugState();
         break;
         break;
-    case SIGIO:
-        m->mDNSHandlePeerEvents = m->mDNSHandlePeerEvents ? mDNSfalse : mDNStrue;
-        LogMsg("SIGIO: Handle AWDL Peer Events %s", m->mDNSHandlePeerEvents ? "Enabled" : "Disabled");
+    case SIGPROF:  mDNS_McastLoggingEnabled = mDNS_McastLoggingEnabled ? mDNSfalse : mDNStrue;
+        LogMsg("SIGPROF: Multicast Logging %s", mDNS_McastLoggingEnabled ? "Enabled" : "Disabled");
+        LogMcastStateInfo(m, mDNSfalse, mDNStrue, mDNStrue);
+        mDNS_McastTracingEnabled = (mDNS_PacketLoggingEnabled && mDNS_McastLoggingEnabled) ? mDNStrue : mDNSfalse;
+        LogMsg("SIGPROF: Multicast Tracing is %s", mDNS_McastTracingEnabled ? "Enabled" : "Disabled");
+        UpdateDebugState();
+        break;
+    case SIGTSTP:  mDNS_LoggingEnabled = mDNS_PacketLoggingEnabled = mDNS_McastLoggingEnabled = mDNS_McastTracingEnabled = mDNSfalse;
+        LogMsg("All mDNSResponder Debug Logging/Tracing Disabled (USR1/USR2/PROF)");
+        UpdateDebugState();
         break;
 
     default: LogMsg("SignalCallback: Unknown signal %d", msg_header->msgh_id); break;
         break;
 
     default: LogMsg("SignalCallback: Unknown signal %d", msg_header->msgh_id); break;
@@ -1916,34 +2014,15 @@ mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void
     KQueueUnlock(m, "Unix Signal");
 }
 
     KQueueUnlock(m, "Unix Signal");
 }
 
-// On 10.2 the MachServerName is DNSServiceDiscoveryServer
-// On 10.3 and later, the MachServerName is com.apple.mDNSResponder
+// MachServerName is com.apple.mDNSResponder (Supported only till 10.9.x)
 mDNSlocal kern_return_t mDNSDaemonInitialize(void)
 {
     mStatus err;
     CFMachPortRef s_port;
     CFRunLoopSourceRef s_rls;
     CFRunLoopSourceRef d_rls;
 mDNSlocal kern_return_t mDNSDaemonInitialize(void)
 {
     mStatus err;
     CFMachPortRef s_port;
     CFRunLoopSourceRef s_rls;
     CFRunLoopSourceRef d_rls;
-
-    // If launchd already created our Mach port for us, then use that, else we create a new one of our own
-    if (m_port != MACH_PORT_NULL)
-        s_port = CFMachPortCreateWithPort(NULL, m_port, DNSserverCallback, NULL, NULL);
-    else
-    {
-        s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL);
-        m_port = CFMachPortGetPort(s_port);
-        kern_return_t status = bootstrap_register(bootstrap_port, "com.apple.mDNSResponder", m_port);
-
-        if (status)
-        {
-            if (status == 1103)
-                LogMsg("bootstrap_register() failed: A copy of the daemon is apparently already running");
-            else
-                LogMsg("bootstrap_register() failed: %d %X %s", status, status, mach_error_string(status));
-            return(status);
-        }
-    }
-
+    
+    s_port = CFMachPortCreateWithPort(NULL, m_port, DNSserverCallback, NULL, NULL);
     CFMachPortRef d_port = CFMachPortCreate(NULL, ClientDeathCallback, NULL, NULL);
 
     err = mDNS_Init(&mDNSStorage, &PlatformStorage,
     CFMachPortRef d_port = CFMachPortCreate(NULL, ClientDeathCallback, NULL, NULL);
 
     err = mDNS_Init(&mDNSStorage, &PlatformStorage,
@@ -1954,7 +2033,7 @@ mDNSlocal kern_return_t mDNSDaemonInitialize(void)
     if (err) { LogMsg("Daemon start: mDNS_Init failed %d", err); return(err); }
 
     client_death_port = CFMachPortGetPort(d_port);
     if (err) { LogMsg("Daemon start: mDNS_Init failed %d", err); return(err); }
 
     client_death_port = CFMachPortGetPort(d_port);
-
+       
     s_rls  = CFMachPortCreateRunLoopSource(NULL, s_port, 0);
     CFRunLoopAddSource(PlatformStorage.CFRunLoop, s_rls, kCFRunLoopDefaultMode);
     CFRelease(s_rls);
     s_rls  = CFMachPortCreateRunLoopSource(NULL, s_port, 0);
     CFRunLoopAddSource(PlatformStorage.CFRunLoop, s_rls, kCFRunLoopDefaultMode);
     CFRelease(s_rls);
@@ -2008,9 +2087,11 @@ mDNSlocal void SignalDispatch(dispatch_source_t source)
     case SIGUSR1:   mDNS_LoggingEnabled = mDNS_LoggingEnabled ? 0 : 1;
         LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled ? "Enabled" : "Disabled");
         WatchDogReportingThreshold = mDNS_LoggingEnabled ? 50 : 250;
     case SIGUSR1:   mDNS_LoggingEnabled = mDNS_LoggingEnabled ? 0 : 1;
         LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled ? "Enabled" : "Disabled");
         WatchDogReportingThreshold = mDNS_LoggingEnabled ? 50 : 250;
+        UpdateDebugState();
         break;
     case SIGUSR2:   mDNS_PacketLoggingEnabled = mDNS_PacketLoggingEnabled ? 0 : 1;
         LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled");
         break;
     case SIGUSR2:   mDNS_PacketLoggingEnabled = mDNS_PacketLoggingEnabled ? 0 : 1;
         LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled");
+        UpdateDebugState();
         break;
     default: LogMsg("SignalCallback: Unknown signal %d", sig); break;
     }
         break;
     default: LogMsg("SignalCallback: Unknown signal %d", sig); break;
     }
@@ -2039,29 +2120,9 @@ mDNSlocal void mDNSSetupSignal(dispatch_queue_t queue, int sig)
 mDNSlocal kern_return_t mDNSDaemonInitialize(void)
 {
     mStatus err;
 mDNSlocal kern_return_t mDNSDaemonInitialize(void)
 {
     mStatus err;
-    CFMachPortRef s_port;
     dispatch_source_t mach_source;
     dispatch_queue_t queue = dispatch_get_main_queue();
 
     dispatch_source_t mach_source;
     dispatch_queue_t queue = dispatch_get_main_queue();
 
-    // If launchd already created our Mach port for us, then use that, else we create a new one of our own
-    if (m_port != MACH_PORT_NULL)
-        s_port = CFMachPortCreateWithPort(NULL, m_port, DNSserverCallback, NULL, NULL);
-    else
-    {
-        s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL);
-        m_port = CFMachPortGetPort(s_port);
-        kern_return_t status = bootstrap_register(bootstrap_port, "com.apple.mDNSResponder", m_port);
-
-        if (status)
-        {
-            if (status == 1103)
-                LogMsg("bootstrap_register() failed: A copy of the daemon is apparently already running");
-            else
-                LogMsg("bootstrap_register() failed: %d %X %s", status, status, mach_error_string(status));
-            return(status);
-        }
-    }
-
     err = mDNS_Init(&mDNSStorage, &PlatformStorage,
                     rrcachestorage, RR_CACHE_SIZE,
                     advertise,
     err = mDNS_Init(&mDNSStorage, &PlatformStorage,
                     rrcachestorage, RR_CACHE_SIZE,
                     advertise,
@@ -2083,7 +2144,7 @@ mDNSlocal kern_return_t mDNSDaemonInitialize(void)
     mDNSSetupSignal(queue, SIGINFO);
     mDNSSetupSignal(queue, SIGUSR1);
     mDNSSetupSignal(queue, SIGUSR2);
     mDNSSetupSignal(queue, SIGINFO);
     mDNSSetupSignal(queue, SIGUSR1);
     mDNSSetupSignal(queue, SIGUSR2);
-    mDNSSetupSignal(queue, SIGIO);
+    mDNSSetupSignal(queue, SIGURG);
 
     // Create a custom handler for doing the housekeeping work. This is either triggered
     // by the timer or an event source
 
     // Create a custom handler for doing the housekeeping work. This is either triggered
     // by the timer or an event source
@@ -2427,6 +2488,16 @@ mDNSlocal mDNSBool AllowSleepNow(mDNS *const m, mDNSs32 now)
            m->p->SleepCookie, ready ? "ready for sleep" : "giving up", now, m->SleepLimit - now);
 
     m->SleepLimit = 0;  // Don't clear m->SleepLimit until after we've logged it above
            m->p->SleepCookie, ready ? "ready for sleep" : "giving up", now, m->SleepLimit - now);
 
     m->SleepLimit = 0;  // Don't clear m->SleepLimit until after we've logged it above
+    m->TimeSlept = mDNSPlatformUTC();
+
+    // accumulate total time awake for this statistics gathering interval
+    if (m->StatStartTime)
+    {
+        m->ActiveStatTime += (m->TimeSlept - m->StatStartTime);
+
+        // indicate this value is invalid until reinitialzed on wakeup
+        m->StatStartTime = 0;
+    }
 
 #if !TARGET_OS_EMBEDDED && defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements)
     if (m->p->IOPMConnection) IOPMConnectionAcknowledgeEventWithOptions(m->p->IOPMConnection, m->p->SleepCookie, opts);
 
 #if !TARGET_OS_EMBEDDED && defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements)
     if (m->p->IOPMConnection) IOPMConnectionAcknowledgeEventWithOptions(m->p->IOPMConnection, m->p->SleepCookie, opts);
@@ -2668,21 +2739,16 @@ mDNSlocal void * KQueueLoop(void *m_param)
 
 mDNSlocal void LaunchdCheckin(void)
 {
 
 mDNSlocal void LaunchdCheckin(void)
 {
-    launch_data_t msg  = launch_data_new_string(LAUNCH_KEY_CHECKIN);
-    launch_data_t resp = launch_msg(msg);
-    launch_data_free(msg);
-    if (!resp) { LogMsg("launch_msg returned NULL"); return; }
-
-    if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO)
-    {
-        int err = launch_data_get_errno(resp);
-        // When running on Tiger with "ServiceIPC = false", we get "err == EACCES" to tell us there's no launchdata to fetch
-        if (err != EACCES) LogMsg("launch_msg returned %d", err);
-        else LogInfo("Launchd provided no launchdata; will open Mach port and Unix Domain Socket explicitly...", err);
+    // Ask launchd for our socket
+    launch_data_t resp_sd = launch_socket_service_check_in();
+    if (!resp_sd)
+    { 
+        LogMsg("launch_socket_service_check_in returned NULL");
+        return; 
     }
     else
     {
     }
     else
     {
-        launch_data_t skts = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_SOCKETS);
+        launch_data_t skts = launch_data_dict_lookup(resp_sd, LAUNCH_JOBKEY_SOCKETS);
         if (!skts) LogMsg("launch_data_dict_lookup LAUNCH_JOBKEY_SOCKETS returned NULL");
         else
         {
         if (!skts) LogMsg("launch_data_dict_lookup LAUNCH_JOBKEY_SOCKETS returned NULL");
         else
         {
@@ -2719,58 +2785,29 @@ mDNSlocal void LaunchdCheckin(void)
                 }
             }
         }
                 }
             }
         }
-
-        launch_data_t ports = launch_data_dict_lookup(resp, "MachServices");
-        if (!ports) LogMsg("launch_data_dict_lookup MachServices returned NULL");
-        else
-        {
-            launch_data_t p = launch_data_dict_lookup(ports, "com.apple.mDNSResponder");
-            if (!p) LogInfo("launch_data_dict_lookup(ports, \"com.apple.mDNSResponder\") returned NULL");
-            else
-            {
-                m_port = launch_data_get_fd(p);
-                LogInfo("Launchd Mach Port: %d", m_port);
-                if (m_port == ~0U) m_port = MACH_PORT_NULL;
-            }
-        }
     }
     }
-    launch_data_free(resp);
+    launch_data_free(resp_sd);
 }
 
 }
 
-mDNSlocal void DropPrivileges(void)
+static mach_port_t RegisterMachService(const char *service_name) 
 {
 {
-    static const char login[] = "_mdnsresponder";
-    struct passwd *pwd = getpwnam(login);
-    if (NULL == pwd)
-        LogMsg("Could not find account name \"%s\". Running as root.", login);
-    else
-    {
-        uid_t uid = pwd->pw_uid;
-        gid_t gid = pwd->pw_gid;
-
-        LogMsg("Started as root. Switching to userid \"%s\".", login);
-
-        if (unlink(MDNS_UDS_SERVERPATH) < 0 && errno != ENOENT) LogMsg("DropPrivileges: Could not unlink \"%s\": (%d) %s", MDNS_UDS_SERVERPATH, errno, strerror(errno));
-        else
-        {
-            static char path[] = "/var/run/mdns/mDNSResponder";
-            char *p = strrchr(path, '/');
-            *p = '\0';
-            if (mkdir(path, 0755) < 0 && errno != EEXIST) LogMsg("DropPrivileges: Could not create directory \"%s\": (%d) %s", path, errno, strerror(errno));
-            else if (chown(path, uid, gid) < 0) LogMsg("DropPrivileges: Could not chown directory \"%s\": (%d) %s", path, errno, strerror(errno));
-            else
-            {
-                *p = '/';
-                if (unlink(path) < 0 && errno != ENOENT) LogMsg("DropPrivileges: Could not unlink \"%s\": (%d) %s", path, errno, strerror(errno));
-                else if (symlink(path, MDNS_UDS_SERVERPATH) < 0) LogMsg("DropPrivileges: Could not symlink \"%s\" -> \"%s\": (%d) %s", MDNS_UDS_SERVERPATH, path, errno, strerror(errno));
-                else LogInfo("DropPrivileges: Created subdirectory and symlink");
-            }
-        }
-
-        if (0 != initgroups(login, gid)) LogMsg("initgroups(\"%s\", %lu) failed.  Continuing.", login,        (unsigned long)gid);
-        if (0 != setgid(gid)) LogMsg("setgid(%lu) failed.  Continuing with group %lu privileges.", (unsigned long)getegid());
-        if (0 != setuid(uid)) LogMsg("setuid(%lu) failed. Continuing as root after all.",          (unsigned long)uid);
-    }
+    mach_port_t port = MACH_PORT_NULL;
+    kern_return_t kr; 
+
+    if (KERN_SUCCESS != (kr = bootstrap_check_in(bootstrap_port, (char *)service_name, &port)))
+    {   
+        LogMsg("RegisterMachService: %d %X %s", kr, kr, mach_error_string(kr));
+        return MACH_PORT_NULL;
+    }   
+    
+    if (KERN_SUCCESS != (kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND)))
+    {   
+        LogMsg("RegisterMachService: %d %X %s", kr, kr, mach_error_string(kr));
+        mach_port_deallocate(mach_task_self(), port);
+        return MACH_PORT_NULL;
+    }   
+
+    return port;
 }
 
 extern int sandbox_init(const char *profile, uint64_t flags, char **errorbuf) __attribute__((weak_import));
 }
 
 extern int sandbox_init(const char *profile, uint64_t flags, char **errorbuf) __attribute__((weak_import));
@@ -2795,13 +2832,15 @@ mDNSexport int main(int argc, char **argv)
     LogMsg("block wastage       %d", 16*1024 - sizeof(CacheEntity) * RR_CACHE_SIZE);
 #endif
 
     LogMsg("block wastage       %d", 16*1024 - sizeof(CacheEntity) * RR_CACHE_SIZE);
 #endif
 
-    if (0 == geteuid()) DropPrivileges();
+    if (0 == geteuid())
+    {
+        LogMsg("mDNSResponder cannot be run as root !! Exiting..");
+        return -1;
+    }
 
     for (i=1; i<argc; i++)
     {
         if (!strcasecmp(argv[i], "-d"                        )) mDNS_DebugMode            = mDNStrue;
 
     for (i=1; i<argc; i++)
     {
         if (!strcasecmp(argv[i], "-d"                        )) mDNS_DebugMode            = mDNStrue;
-        if (!strcasecmp(argv[i], "-launchd"                  )) started_via_launchdaemon  = mDNStrue;
-        if (!strcasecmp(argv[i], "-launchdaemon"             )) started_via_launchdaemon  = mDNStrue;
         if (!strcasecmp(argv[i], "-NoMulticastAdvertisements")) advertise                 = mDNS_Init_DontAdvertiseLocalAddresses;
         if (!strcasecmp(argv[i], "-DisableSleepProxyClient"  )) DisableSleepProxyClient   = mDNStrue;
         if (!strcasecmp(argv[i], "-DebugLogging"             )) mDNS_LoggingEnabled       = mDNStrue;
         if (!strcasecmp(argv[i], "-NoMulticastAdvertisements")) advertise                 = mDNS_Init_DontAdvertiseLocalAddresses;
         if (!strcasecmp(argv[i], "-DisableSleepProxyClient"  )) DisableSleepProxyClient   = mDNStrue;
         if (!strcasecmp(argv[i], "-DebugLogging"             )) mDNS_LoggingEnabled       = mDNStrue;
@@ -2821,35 +2860,22 @@ mDNSexport int main(int argc, char **argv)
 
     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
 
     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
-    signal(SIGPIPE, SIG_IGN  );     // Don't want SIGPIPE signals -- we'll handle EPIPE errors directly
+    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
     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
-    signal(SIGIO, HandleSIG);       // (Debugging) Toggle AWDL Peer Event Handling
+    signal(SIGURG,  HandleSIG);     // (Debugging) Toggle Opportunistic Caching
+    signal(SIGPROF, HandleSIG);     // (Debugging) Toggle Multicast Logging
+    signal(SIGTSTP, HandleSIG);     // (Debugging) Disable all Debug Logging (USR1/USR2/PROF)
 
 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 
     mDNSStorage.p = &PlatformStorage;   // Make sure mDNSStorage.p is set up, because validatelists uses it
 
 #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 
     mDNSStorage.p = &PlatformStorage;   // Make sure mDNSStorage.p is set up, because validatelists uses it
+    // Need to Start XPC Server Before LaunchdCheckin() (Reason: rdar11023750)
+    xpc_server_init();
     LaunchdCheckin();
 
     LaunchdCheckin();
 
-    // Register the server with mach_init for automatic restart only during normal (non-debug) mode
-    if (!mDNS_DebugMode && !started_via_launchdaemon)
-    {
-        registerBootstrapService();
-        if (!restarting_via_mach_init) exit(0); // mach_init will restart us immediately as a daemon
-        int fd = open(_PATH_DEVNULL, O_RDWR, 0);
-        if (fd < 0) LogMsg("open(_PATH_DEVNULL, O_RDWR, 0) failed errno %d (%s)", errno, strerror(errno));
-        else
-        {
-            // Avoid unnecessarily duplicating a file descriptor to itself
-            if (fd != STDIN_FILENO) if (dup2(fd, STDIN_FILENO)  < 0) LogMsg("dup2(fd, STDIN_FILENO)  failed errno %d (%s)", errno, strerror(errno));
-            if (fd != STDOUT_FILENO) if (dup2(fd, STDOUT_FILENO) < 0) LogMsg("dup2(fd, STDOUT_FILENO) failed errno %d (%s)", errno, strerror(errno));
-            if (fd != STDERR_FILENO) if (dup2(fd, STDERR_FILENO) < 0) LogMsg("dup2(fd, STDERR_FILENO) failed errno %d (%s)", errno, strerror(errno));
-            if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO) (void)close(fd);
-        }
-    }
-
 #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 
     // Create the kqueue, mutex and thread to support KQSockets
 #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 
     // Create the kqueue, mutex and thread to support KQSockets
@@ -2885,7 +2911,15 @@ mDNSexport int main(int argc, char **argv)
         uint64_t sandbox_flags = SANDBOX_NAMED;
 
         int sandbox_err = sandbox_init("mDNSResponder", sandbox_flags, &sandbox_msg);
         uint64_t sandbox_flags = SANDBOX_NAMED;
 
         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); }
+        if (sandbox_err)
+        {
+            LogMsg("WARNING: sandbox_init error %s", sandbox_msg);
+            // If we have errors in the sandbox during development, to prevent
+            // exiting, uncomment the following line.
+            //sandbox_free_error(sandbox_msg);
+
+            errx(EX_OSERR, "sandbox_init() failed: %s", sandbox_msg);
+        }
         else LogInfo("Now running under Apple Sandbox restrictions");
     }
 #endif // MDNS_NO_SANDBOX
         else LogInfo("Now running under Apple Sandbox restrictions");
     }
 #endif // MDNS_NO_SANDBOX
@@ -2897,13 +2931,32 @@ mDNSexport int main(int argc, char **argv)
     vproc_transaction_t vt = vproc_transaction_begin(NULL);
     if (vt) vproc_transaction_end(NULL, vt);
 
     vproc_transaction_t vt = vproc_transaction_begin(NULL);
     if (vt) vproc_transaction_end(NULL, vt);
 
+    m_port = RegisterMachService(kmDNSResponderServName);
+    // We should ALWAYS receive our Mach port from RegisterMachService() but sanity check before initializing daemon 
+    if (m_port == MACH_PORT_NULL)
+    {
+        LogMsg("! MACH PORT IS NULL ! bootstrap_checkin failed to give a mach port");
+        return -1;
+    }    
+
     status = mDNSDaemonInitialize();
     if (status) { LogMsg("Daemon start: mDNSDaemonInitialize failed"); goto exit; }
 
     status = udsserver_init(launchd_fds, launchd_fds_count);
     if (status) { LogMsg("Daemon start: udsserver_init failed"); goto exit; }
 
     status = mDNSDaemonInitialize();
     if (status) { LogMsg("Daemon start: mDNSDaemonInitialize failed"); goto exit; }
 
     status = udsserver_init(launchd_fds, launchd_fds_count);
     if (status) { LogMsg("Daemon start: udsserver_init failed"); goto exit; }
 
+    log_client = asl_open(NULL, "mDNSResponder", 0);
+    log_msg = asl_new(ASL_TYPE_MSG);
+       
+#if TARGET_OS_EMBEDDED
+    _scprefs_observer_watch(scprefs_observer_type_global, kmDNSResponderPrefIDStr, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),  
+                                                ^{   
+                                                    Prefschanged();
+                                                 });
+#endif
+
     mDNSMacOSXNetworkChanged(&mDNSStorage);
     mDNSMacOSXNetworkChanged(&mDNSStorage);
+    UpdateDebugState();
 
 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
     LogInfo("Daemon Start: Using LibDispatch");
 
 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
     LogInfo("Daemon Start: Using LibDispatch");
@@ -2925,7 +2978,6 @@ mDNSexport int main(int argc, char **argv)
     LogMsg("%s exiting", mDNSResponderVersionString);
 
 exit:
     LogMsg("%s exiting", mDNSResponderVersionString);
 
 exit:
-    if (!mDNS_DebugMode && !started_via_launchdaemon) destroyBootstrapService();
     return(status);
 }
 
     return(status);
 }
 
diff --git a/mDNSMacOSX/dnsctl-entitlements.plist b/mDNSMacOSX/dnsctl-entitlements.plist
new file mode 100644 (file)
index 0000000..fb9c3a3
--- /dev/null
@@ -0,0 +1,8 @@
+<?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>com.apple.mDNSResponder.dnsproxy</key>
+    <true/>
+</dict>
+</plist>
index f90d5ce9c88d5160efadd1dd894109bcfbbeafa8..2e463b0a8acbe49af71c14503bdafe7529075aa9 100644 (file)
@@ -19,8 +19,6 @@ ERROR(kmDNSHelperCommunicationFailed,             "Mach communication failed")
 ERROR(kmDNSHelperNotAuthorized,                   "Not authorized")
 ERROR(kmDNSHelperCreationFailed,                  "Object creation failed")
 ERROR(kmDNSHelperInvalidPList,                    "Invalid property list")
 ERROR(kmDNSHelperNotAuthorized,                   "Not authorized")
 ERROR(kmDNSHelperCreationFailed,                  "Object creation failed")
 ERROR(kmDNSHelperInvalidPList,                    "Invalid property list")
-ERROR(kmDNSHelperDynamicStoreFailed,              "Could not create dynamic store session")
-ERROR(kmDNSHelperDynamicStoreSetFailed,           "Could not set dynamic store configuration")
 ERROR(kmDNSHelperInvalidNameKey,                  "Invalid name key")
 ERROR(kmDNSHelperInvalidConfigKey,                "Invalid configuration key")
 ERROR(kmDNSHelperTypeError,                       "Object was not of expected type")
 ERROR(kmDNSHelperInvalidNameKey,                  "Invalid name key")
 ERROR(kmDNSHelperInvalidConfigKey,                "Invalid configuration key")
 ERROR(kmDNSHelperTypeError,                       "Object was not of expected type")
index d3610a4bd71b0c751ce58849665322fd27c10d6d..52779e8546baf5ca76043ac56134f1297e99b932 100644 (file)
@@ -17,9 +17,6 @@
 
 #define _FORTIFY_SOURCE 2
 
 
 #define _FORTIFY_SOURCE 2
 
-// We set VERSION_MIN_REQUIRED to 10.4 to avoid "bootstrap_register is deprecated" warnings from bootstrap.h
-#define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_4
-
 #include <CoreFoundation/CoreFoundation.h>
 #include <sys/cdefs.h>
 #include <sys/time.h>
 #include <CoreFoundation/CoreFoundation.h>
 #include <sys/cdefs.h>
 #include <sys/time.h>
 #include <vproc.h>
 
 #if TARGET_OS_EMBEDDED
 #include <vproc.h>
 
 #if TARGET_OS_EMBEDDED
-#include <bootstrap_priv.h>
 #define NO_SECURITYFRAMEWORK 1
 #define NO_SECURITYFRAMEWORK 1
-
-#define bootstrap_register(A,B,C) bootstrap_register2((A),(B),(C),0)
 #endif
 
 #ifndef LAUNCH_JOBKEY_MACHSERVICES
 #endif
 
 #ifndef LAUNCH_JOBKEY_MACHSERVICES
@@ -188,62 +182,25 @@ static int initialize_timer()
     return err;
 }
 
     return err;
 }
 
-static mach_port_t checkin(char *service_name)
-{
-    kern_return_t kr = KERN_SUCCESS;
-    mach_port_t port = MACH_PORT_NULL;
-    launch_data_t msg = NULL, reply = NULL, datum = NULL;
-
-    if (NULL == (msg = launch_data_new_string(LAUNCH_KEY_CHECKIN)))
-    { helplog(ASL_LEVEL_ERR, "Could not create checkin message for launchd."); goto fin; }
-    if (NULL == (reply = launch_msg(msg)))
-    { helplog(ASL_LEVEL_ERR, "Could not message launchd."); goto fin; }
-    if (LAUNCH_DATA_ERRNO == launch_data_get_type(reply))
-    {
-        if (launch_data_get_errno(reply) == EACCES) { launch_data_free(msg); launch_data_free(reply); return(MACH_PORT_NULL); }
-        helplog(ASL_LEVEL_ERR, "Launchd checkin failed: %s.", strerror(launch_data_get_errno(reply))); goto fin;
-    }
-    if (NULL == (datum = launch_data_dict_lookup(reply, LAUNCH_JOBKEY_MACHSERVICES)) || LAUNCH_DATA_DICTIONARY != launch_data_get_type(datum))
-    { helplog(ASL_LEVEL_ERR, "Launchd reply does not contain %s dictionary.", LAUNCH_JOBKEY_MACHSERVICES); goto fin; }
-    if (NULL == (datum = launch_data_dict_lookup(datum, service_name)) || LAUNCH_DATA_MACHPORT != launch_data_get_type(datum))
-    { helplog(ASL_LEVEL_ERR, "Launchd reply does not contain %s Mach port.", service_name); goto fin; }
-    if (MACH_PORT_NULL == (port = launch_data_get_machport(datum)))
-    { helplog(ASL_LEVEL_ERR, "Launchd gave me a null Mach port."); goto fin; }
-    if (KERN_SUCCESS != (kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND)))
-    { helplog(ASL_LEVEL_ERR, "mach_port_insert_right: %d %X %s", kr, kr, mach_error_string(kr)); goto fin; }
-
-fin:
-    if (NULL != msg) launch_data_free(msg);
-    if (NULL != reply) launch_data_free(reply);
-    if (MACH_PORT_NULL == port) exit(EXIT_FAILURE);
-    return port;
-}
-
 static mach_port_t register_service(const char *service_name)
 {
     mach_port_t port = MACH_PORT_NULL;
     kern_return_t kr;
 
 static mach_port_t register_service(const char *service_name)
 {
     mach_port_t port = MACH_PORT_NULL;
     kern_return_t kr;
 
-    if (KERN_SUCCESS == (kr = bootstrap_check_in(bootstrap_port, (char *)service_name, &port)))
+    if (KERN_SUCCESS != (kr = bootstrap_check_in(bootstrap_port, (char *)service_name, &port)))
     {
     {
-        if (KERN_SUCCESS != (kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND)))
-            helplog(ASL_LEVEL_ERR, "mach_port_insert_right: %d %X %s", kr, kr, mach_error_string(kr));
-        else
-            return port;
+        helplog(ASL_LEVEL_ERR, "bootstrap_check_in: %d %X %s", kr, kr, mach_error_string(kr));
+        return MACH_PORT_NULL;
     }
     }
-    if (KERN_SUCCESS != (kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port)))
-    { helplog(ASL_LEVEL_ERR, "mach_port_allocate: %d %X %s", kr, kr, mach_error_string(kr)); goto error; }
+    
     if (KERN_SUCCESS != (kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND)))
     if (KERN_SUCCESS != (kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND)))
-    { helplog(ASL_LEVEL_ERR, "mach_port_insert_right: %d %X %s", kr, kr, mach_error_string(kr)); goto error; }
-
-    // XXX bootstrap_register does not modify its second argument, but the prototype does not include const.
-    if (KERN_SUCCESS != (kr = bootstrap_register(bootstrap_port, (char *)service_name, port)))
-    { helplog(ASL_LEVEL_ERR, "bootstrap_register failed: %s %d %X %s", service_name, kr, kr, mach_error_string(kr)); goto error; }
+    {
+        helplog(ASL_LEVEL_ERR, "mach_port_insert_right: %d %X %s", kr, kr, mach_error_string(kr));
+        mach_port_deallocate(mach_task_self(), port);
+        return MACH_PORT_NULL;
+    }
 
     return port;
 
     return port;
-error:
-    if (MACH_PORT_NULL != port) mach_port_deallocate(mach_task_self(), port);
-    return MACH_PORT_NULL;
 }
 
 int main(int ac, char *av[])
 }
 
 int main(int ac, char *av[])
@@ -281,12 +238,9 @@ int main(int ac, char *av[])
     // Explicitly ensure that our Keychain operations utilize the system domain.
     if (opt_debug) SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
 #endif
     // Explicitly ensure that our Keychain operations utilize the system domain.
     if (opt_debug) SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
 #endif
-    gPort = checkin(kmDNSHelperServiceName);
+    gPort = register_service(kmDNSHelperServiceName);
     if (!gPort)
     if (!gPort)
-    {
-        helplog(ASL_LEVEL_ERR, "Launchd provided no launchdata; will open Mach port explicitly");
-        gPort = register_service(kmDNSHelperServiceName);
-    }
+        exit(EXIT_FAILURE);
 
     if (maxidle) actualidle = maxidle;
 
 
     if (maxidle) actualidle = maxidle;
 
index 9b1afaf5f084fba1507d36ef34c2b7c504e77ba7..29fd9aed8079b5aa820ac62f767d6ca8a3df72d3 100644 (file)
 #include "mDNSDebug.h"
 #include "helper.h"
 #include "helpermsg.h"
 #include "mDNSDebug.h"
 #include "helper.h"
 #include "helpermsg.h"
+#include <dispatch/dispatch.h>
+#include <arpa/inet.h>
+
+//
+// Implementation Notes about the HelperQueue:
+//
+// To prevent blocking the main queue, all communications with mDNSResponderHelper should happen on
+// HelperQueue. There are a few calls which are still synchronous and needs to be handled separately
+// case by case.
+//
+// When spawning off the work to the HelperQueue, any arguments that are pointers need to be copied
+// explicitly as they may cease to exist after the call returns. From within the block that is scheduled,
+// arrays defined on the stack can't be referenced and hence it is enclosed them in a struct. If the array is
+// an argument to the function, the blocks can reference them as they are passed in as pointers. But care should
+// be taken to copy them locally as they may cease to exist when the function returns.
+//
+static dispatch_queue_t HelperQueue;
 
 #define ERROR(x, y) y,
 static const char *errorstring[] =
 
 #define ERROR(x, y) y,
 static const char *errorstring[] =
@@ -32,6 +49,17 @@ static const char *errorstring[] =
 };
 #undef ERROR
 
 };
 #undef ERROR
 
+mDNSexport mStatus mDNSHelperInit()
+{
+    HelperQueue = dispatch_queue_create("com.apple.mDNSResponder.HelperQueue", NULL);
+    if (HelperQueue == NULL)
+    {
+        LogMsg("dispatch_queue_create: Helper queue NULL");
+        return mStatus_NoMemoryErr;
+    }
+    return mStatus_NoError;
+}
+
 static mach_port_t getHelperPort(int retry)
 {
     static mach_port_t port = MACH_PORT_NULL;
 static mach_port_t getHelperPort(int retry)
 {
     static mach_port_t port = MACH_PORT_NULL;
@@ -71,71 +99,45 @@ const char *mDNSHelperError(int err)
 
 void mDNSPreferencesSetName(int key, domainlabel *old, domainlabel *new)
 {
 
 void mDNSPreferencesSetName(int key, domainlabel *old, domainlabel *new)
 {
-    kern_return_t kr = KERN_FAILURE;
-    int retry = 0;
-    int err = 0;
-    char oldname[MAX_DOMAIN_LABEL+1] = {0};
-    char newname[MAX_DOMAIN_LABEL+1] = {0};
-    ConvertDomainLabelToCString_unescaped(old, oldname);
-    if (new) ConvertDomainLabelToCString_unescaped(new, newname);
+    struct {
+        char oldname[MAX_DOMAIN_LABEL+1];
+        char newname[MAX_DOMAIN_LABEL+1];
+    } names;
 
 
-    MACHRETRYLOOP_BEGIN(kr, retry, err, fin);
-    kr = proxy_mDNSPreferencesSetName(getHelperPort(retry), key, oldname, newname);
-    MACHRETRYLOOP_END(kr, retry, err, fin);
+    mDNSPlatformMemZero(names.oldname, MAX_DOMAIN_LABEL + 1);
+    mDNSPlatformMemZero(names.newname, MAX_DOMAIN_LABEL + 1);
 
 
-fin:
-    (void)err;
-}
+    ConvertDomainLabelToCString_unescaped(old, names.oldname);
+    if (new) ConvertDomainLabelToCString_unescaped(new, names.newname);
+    dispatch_async(HelperQueue, ^{
 
 
-void mDNSDynamicStoreSetConfig(int key, const char *subkey, CFPropertyListRef value)
-{
-    CFWriteStreamRef stream = NULL;
-    CFDataRef bytes = NULL;
-    kern_return_t kr = KERN_FAILURE;
-    int retry = 0;
-    int err = 0;
+        kern_return_t kr = KERN_FAILURE;
+        int retry = 0;
+        int err = 0;
 
 
-    if (NULL == (stream = CFWriteStreamCreateWithAllocatedBuffers(NULL, NULL)))
-    {
-        err = kmDNSHelperCreationFailed;
-        LogMsg("%s: CFWriteStreamCreateWithAllocatedBuffers failed", __func__);
-        goto fin;
-    }
-    CFWriteStreamOpen(stream);
-    if (0 == CFPropertyListWriteToStream(value, stream, kCFPropertyListBinaryFormat_v1_0, NULL))
-    {
-        err = kmDNSHelperPListWriteFailed;
-        LogMsg("%s: CFPropertyListWriteToStream failed", __func__);
-        goto fin;
-    }
-    if (NULL == (bytes = CFWriteStreamCopyProperty(stream, kCFStreamPropertyDataWritten)))
-    {
-        err = kmDNSHelperCreationFailed;
-        LogMsg("%s: CFWriteStreamCopyProperty failed", __func__);
-        goto fin;
-    }
-    CFWriteStreamClose(stream);
-    CFRelease(stream);
-    stream = NULL;
-    MACHRETRYLOOP_BEGIN(kr, retry, err, fin);
-    kr = proxy_mDNSDynamicStoreSetConfig(getHelperPort(retry), key, subkey ? subkey : "", (vm_offset_t)CFDataGetBytePtr(bytes), CFDataGetLength(bytes));
-    MACHRETRYLOOP_END(kr, retry, err, fin);
+        LogInfo("%s: oldname %s newname %s", __func__, names.oldname, names.newname);
+        MACHRETRYLOOP_BEGIN(kr, retry, err, fin);
+        kr = proxy_mDNSPreferencesSetName(getHelperPort(retry), key, names.oldname, names.newname);
+        MACHRETRYLOOP_END(kr, retry, err, fin);
 
 fin:
 
 fin:
-    if (NULL != stream) { CFWriteStreamClose(stream); CFRelease(stream); }
-    if (NULL != bytes) CFRelease(bytes);
-    (void)err;
+        (void)err;
+    });
 }
 
 void mDNSRequestBPF(void)
 {
 }
 
 void mDNSRequestBPF(void)
 {
-    kern_return_t kr = KERN_FAILURE;
-    int retry = 0, err = 0;
-    MACHRETRYLOOP_BEGIN(kr, retry, err, fin);
-    kr = proxy_mDNSRequestBPF(getHelperPort(retry));
-    MACHRETRYLOOP_END(kr, retry, err, fin);
+    dispatch_async(HelperQueue, ^{
+
+        kern_return_t kr = KERN_FAILURE;
+        int retry = 0, err = 0;
+        LogInfo("%s: BPF", __func__);
+        MACHRETRYLOOP_BEGIN(kr, retry, err, fin);
+        kr = proxy_mDNSRequestBPF(getHelperPort(retry));
+        MACHRETRYLOOP_END(kr, retry, err, fin);
 fin:
 fin:
-    (void)err;
+        (void)err;
+    });
 }
 
 int mDNSPowerRequest(int key, int interval)
 }
 
 int mDNSPowerRequest(int key, int interval)
@@ -162,13 +164,51 @@ fin:
 
 void mDNSNotify(const char *title, const char *msg) // Both strings are UTF-8 text
 {
 
 void mDNSNotify(const char *title, const char *msg) // Both strings are UTF-8 text
 {
-    kern_return_t kr = KERN_FAILURE;
-    int retry = 0, err = 0;
-    MACHRETRYLOOP_BEGIN(kr, retry, err, fin);
-    kr = proxy_mDNSNotify(getHelperPort(retry), title, msg);
-    MACHRETRYLOOP_END(kr, retry, err, fin);
+    char *titleCopy = NULL;
+    char *msgCopy = NULL;
+
+    if (title)
+    {
+        int len = strlen(title);
+        titleCopy = mDNSPlatformMemAllocate(len + 1);
+        if (!titleCopy)
+        {
+            LogMsg("mDNSNotify: titleCopy NULL for %s", msg);
+            return;
+        }
+        mDNSPlatformMemCopy(titleCopy, title, len);
+        titleCopy[len] = 0;
+    }
+    if (msg)
+    {
+        int len = strlen(msg);
+        msgCopy = mDNSPlatformMemAllocate(len + 1);
+        if (!msgCopy)
+        {
+            LogMsg("mDNSNotify: msgCopy NULL for %s", msg);
+            return;
+        }
+        mDNSPlatformMemCopy(msgCopy, msg, len);
+        msgCopy[len] = 0;
+    }
+        
+    dispatch_async(HelperQueue, ^{
+
+        kern_return_t kr = KERN_FAILURE;
+        int retry = 0, err = 0;
+
+        LogInfo("%s: title %s, msg %s", __func__, titleCopy, msgCopy);
+
+        MACHRETRYLOOP_BEGIN(kr, retry, err, fin);
+        kr = proxy_mDNSNotify(getHelperPort(retry), titleCopy, msgCopy);
+        MACHRETRYLOOP_END(kr, retry, err, fin);
 fin:
 fin:
-    (void)err;
+        if (titleCopy)
+            mDNSPlatformMemFree(titleCopy);
+        if (msgCopy)
+            mDNSPlatformMemFree(msgCopy);
+        (void)err;
+    });
 }
 
 int mDNSKeychainGetSecrets(CFArrayRef *result)
 }
 
 int mDNSKeychainGetSecrets(CFArrayRef *result)
@@ -215,19 +255,34 @@ fin:
 
 void mDNSConfigureServer(int updown, const char *const prefix, const domainname *const fqdn)
 {
 
 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 + 10] = { 0 }; // Assume the prefix is no larger than 10 chars
+    struct
+    {
+        // Assume the prefix is no larger than 10 chars
+        char fqdnStr[MAX_ESCAPED_DOMAIN_NAME + 10];
+    } name;
+
+    mDNSPlatformMemZero(name.fqdnStr, MAX_DOMAIN_LABEL + 10);
+
     if (fqdn)
     {
     if (fqdn)
     {
-        mDNSPlatformStrCopy(fqdnStr, prefix);
-        ConvertDomainNameToCString(fqdn, fqdnStr + mDNSPlatformStrLen(prefix));
+        mDNSPlatformStrCopy(name.fqdnStr, prefix);
+        ConvertDomainNameToCString(fqdn, name.fqdnStr + mDNSPlatformStrLen(prefix));
     }
     }
-    MACHRETRYLOOP_BEGIN(kr, retry, err, fin);
-    kr = proxy_mDNSConfigureServer(getHelperPort(retry), updown, fqdnStr);
-    MACHRETRYLOOP_END(kr, retry, err, fin);
+
+    dispatch_async(HelperQueue, ^{
+
+        kern_return_t kr = KERN_SUCCESS;
+        int retry = 0, err = 0;
+
+        LogInfo("%s: fqdnStr %s", __func__, name.fqdnStr);
+
+        MACHRETRYLOOP_BEGIN(kr, retry, err, fin);
+        kr = proxy_mDNSConfigureServer(getHelperPort(retry), updown, name.fqdnStr);
+        MACHRETRYLOOP_END(kr, retry, err, fin);
 fin:
 fin:
-    (void)err;
+        (void)err;
+
+    });
 }
 
 int mDNSAutoTunnelSetKeys(int replacedelete, v6addr_t local_inner,
 }
 
 int mDNSAutoTunnelSetKeys(int replacedelete, v6addr_t local_inner,
@@ -251,47 +306,191 @@ fin:
 
 void mDNSSendWakeupPacket(unsigned ifid, char *eth_addr, char *ip_addr, int iteration)
 {
 
 void mDNSSendWakeupPacket(unsigned ifid, char *eth_addr, char *ip_addr, int iteration)
 {
-    kern_return_t kr = KERN_SUCCESS;
-    int retry = 0, err = 0;
-    MACHRETRYLOOP_BEGIN(kr, retry, err, fin);
-    kr = proxy_mDNSSendWakeupPacket(getHelperPort(retry), ifid, eth_addr, ip_addr, iteration);
-    MACHRETRYLOOP_END(kr, retry, err, fin);
+    char *ip_addr_copy = NULL;
+    char *eth_addr_copy = NULL;
+
+    if (eth_addr)
+    {
+        int len = strlen(eth_addr);
+        eth_addr_copy = mDNSPlatformMemAllocate(len + 1);
+        if (!eth_addr_copy)
+        {
+            LogMsg("mDNSSendWakeupPacket: eth_addr_copy NULL for %s", eth_addr);
+            return;
+        }
+        mDNSPlatformMemCopy(eth_addr_copy, eth_addr, len);
+        eth_addr_copy[len] = 0;
+    }
+    if (ip_addr)
+    {
+        int len = strlen(ip_addr);
+        ip_addr_copy = mDNSPlatformMemAllocate(len + 1);
+        if (!ip_addr_copy)
+        {
+            LogMsg("mDNSSendWakeupPacket: ip_addr_copy NULL for %s", ip_addr);
+            return;
+        }
+        mDNSPlatformMemCopy(ip_addr_copy, ip_addr, len);
+        ip_addr_copy[len] = 0;
+    }
+    dispatch_async(HelperQueue, ^{
+
+        kern_return_t kr = KERN_SUCCESS;
+        int retry = 0, err = 0;
+
+        LogInfo("%s: Entered ethernet address %s, ip address %s", __func__, eth_addr_copy, ip_addr_copy);
+
+        MACHRETRYLOOP_BEGIN(kr, retry, err, fin);
+        kr = proxy_mDNSSendWakeupPacket(getHelperPort(retry), ifid, eth_addr_copy, ip_addr_copy, iteration);
+        MACHRETRYLOOP_END(kr, retry, err, fin);
 fin:
 fin:
-    (void) err;
+        if (eth_addr_copy)
+            mDNSPlatformMemFree(eth_addr_copy);
+        if (ip_addr_copy)
+            mDNSPlatformMemFree(ip_addr_copy);
+        (void) err;
+    });
 }
 
 void mDNSPacketFilterControl(uint32_t command, char * ifname, uint32_t count, pfArray_t portArray, pfArray_t protocolArray)
 {
 }
 
 void mDNSPacketFilterControl(uint32_t command, char * ifname, uint32_t count, pfArray_t portArray, pfArray_t protocolArray)
 {
-    kern_return_t kr = KERN_SUCCESS;
-    int retry = 0, err = 0;
-    MACHRETRYLOOP_BEGIN(kr, retry, err, fin);
-    kr = proxy_mDNSPacketFilterControl(getHelperPort(retry), command, ifname, count, portArray, protocolArray);
-    MACHRETRYLOOP_END(kr, retry, err, fin);
+    struct
+    {
+        pfArray_t portArray;
+        pfArray_t protocolArray;
+    } pfa;
+    char *ifnameCopy = NULL;
+    
+    mDNSPlatformMemCopy(pfa.portArray, portArray, sizeof(pfArray_t));
+    mDNSPlatformMemCopy(pfa.protocolArray, protocolArray, sizeof(pfArray_t));
+    if (ifname)
+    {
+        int len = strlen(ifname);
+        ifnameCopy = mDNSPlatformMemAllocate(len + 1);
+        if (!ifnameCopy)
+        {
+            LogMsg("mDNSPacketFilterControl: ifnameCopy NULL");
+            return;
+        }
+        mDNSPlatformMemCopy(ifnameCopy, ifname, len);
+        ifnameCopy[len] = 0;
+    }
+    dispatch_async(HelperQueue, ^{
+
+        kern_return_t kr = KERN_SUCCESS;
+        int retry = 0, err = 0;
+
+        LogInfo("%s, ifname %s", __func__, ifnameCopy);
+
+        MACHRETRYLOOP_BEGIN(kr, retry, err, fin);
+        kr = proxy_mDNSPacketFilterControl(getHelperPort(retry), command, ifnameCopy, count, (uint16_t *)pfa.portArray, (uint16_t *)pfa.protocolArray);
+        MACHRETRYLOOP_END(kr, retry, err, fin);
+fin:
+        if (ifnameCopy)
+            mDNSPlatformMemFree(ifnameCopy);
+        (void) err;
+    });
+}
+
+void mDNSSendKeepalive(v6addr_t sadd, v6addr_t dadd, uint16_t lport, uint16_t rport, unsigned seq, unsigned ack, uint16_t win)
+{
+    struct
+    {
+        v6addr_t sadd;
+        v6addr_t dadd;
+    } addr;
+
+    mDNSPlatformMemCopy(addr.sadd, sadd, sizeof(v6addr_t));
+    mDNSPlatformMemCopy(addr.dadd, dadd, sizeof(v6addr_t));
+
+    dispatch_async(HelperQueue, ^{
+
+        kern_return_t kr = KERN_FAILURE;
+        int retry = 0, err = 0;
+        char buf1[INET6_ADDRSTRLEN];
+        char buf2[INET6_ADDRSTRLEN];
+
+        buf1[0] = 0;
+        buf2[0] = 0;
+
+        inet_ntop(AF_INET6, addr.sadd, buf1, sizeof(buf1));
+        inet_ntop(AF_INET6, addr.dadd, buf2, sizeof(buf2));
+        LogInfo("%s: sadd is %s, dadd is %s", __func__, buf1, buf2);
+
+        MACHRETRYLOOP_BEGIN(kr, retry, err, fin);
+        kr = proxy_mDNSSendKeepalive(getHelperPort(retry), (uint8_t *)addr.sadd, (uint8_t *)addr.dadd, lport, rport, seq, ack, win);
+        MACHRETRYLOOP_END(kr, retry, err, fin);
 fin:
 fin:
-    (void) err;
+        (void) err;
+    });
 }
 
 }
 
-int mDNSSendKeepalive(v6addr_t sadd, v6addr_t dadd, uint16_t lport, uint16_t rport, unsigned seq, unsigned ack, uint16_t win)
+int mDNSRetrieveTCPInfo(int family, v6addr_t laddr, uint16_t lport, v6addr_t raddr, uint16_t rport, uint32_t *seq, uint32_t *ack, uint16_t *win, int32_t *intfid)
 {
     kern_return_t kr = KERN_FAILURE;
     int retry = 0, err = 0;
     MACHRETRYLOOP_BEGIN(kr, retry, err, fin);
 {
     kern_return_t kr = KERN_FAILURE;
     int retry = 0, err = 0;
     MACHRETRYLOOP_BEGIN(kr, retry, err, fin);
-    kr = proxy_mDNSSendKeepalive(getHelperPort(retry), sadd, dadd, lport, rport, seq, ack, win);
+    kr = proxy_mDNSRetrieveTCPInfo(getHelperPort(retry), family, (uint8_t *)laddr, lport, (uint8_t *)raddr, rport, seq, ack, win, intfid);
     MACHRETRYLOOP_END(kr, retry, err, fin);
 fin:
     return err;
 }
 
     MACHRETRYLOOP_END(kr, retry, err, fin);
 fin:
     return err;
 }
 
-
-int mDNSInterfaceAdvtIoctl(const char *ifname, int op)
+void mDNSGetRemoteMAC(mDNS *const m, int family, v6addr_t raddr)
 {
 {
-    kern_return_t kr = KERN_FAILURE;
-    int retry = 0, err = 0;
+    struct {
+        v6addr_t addr;
+    } dst;
 
 
-    MACHRETRYLOOP_BEGIN(kr, retry, err, fin);
-    kr = proxy_mDNSInterfaceAdvtIoctl(getHelperPort(retry), ifname, op);
-    MACHRETRYLOOP_END(kr, retry, err, fin);
+    mDNSPlatformMemCopy(dst.addr, raddr, sizeof(v6addr_t));
+    dispatch_async(HelperQueue, ^{
+        kern_return_t        kr    = KERN_FAILURE;
+        int                  retry = 0, err = 0;
+        ethaddr_t            eth;
+        IPAddressMACMapping *addrMapping;
 
 
+        MACHRETRYLOOP_BEGIN(kr, retry, err, fin);
+        kr = proxy_mDNSGetRemoteMAC(getHelperPort(retry), family, (uint8_t *)dst.addr, eth);
+        MACHRETRYLOOP_END(kr, retry, err, fin);
+        // If the call to get the remote MAC address succeeds, allocate and copy
+        // the values and schedule a task to update the MAC address in the TCP Keepalive record.
+        if (kr == KERN_SUCCESS)
+        {
+            addrMapping = (IPAddressMACMapping *)malloc(sizeof(IPAddressMACMapping));
+            snprintf(addrMapping->ethaddr, sizeof(addrMapping->ethaddr), "%02x:%02x:%02x:%02x:%02x:%02x",
+                     eth[0], eth[1], eth[2], eth[3], eth[4], eth[5]);
+            if (family == AF_INET)
+            {
+                addrMapping->ipaddr.type = mDNSAddrType_IPv4;
+                mDNSPlatformMemCopy(addrMapping->ipaddr.ip.v4.b,  dst.addr, sizeof(v6addr_t));
+            }
+            else
+            {
+                addrMapping->ipaddr.type = mDNSAddrType_IPv6;
+                mDNSPlatformMemCopy(addrMapping->ipaddr.ip.v6.b,  dst.addr, sizeof(v6addr_t));
+            }
+            mDNSPlatformDispatchAsync(m, addrMapping, UpdateRMACCallback);
+        }
 fin:
 fin:
-    return err;
+            (void) err;
+    });
+
+}
+
+void mDNSStoreSPSMACAddress(int family, v6addr_t spsaddr, char *ifname)
+{
+    struct {
+        v6addr_t saddr;
+    } addr;
+    mDNSPlatformMemCopy(addr.saddr, spsaddr, sizeof(v6addr_t));
+
+    dispatch_async(HelperQueue, ^{
+        kern_return_t kr = KERN_FAILURE;
+        int retry = 0, err = 0;
+        MACHRETRYLOOP_BEGIN(kr, retry, err, fin);
+        kr = proxy_mDNSStoreSPSMACAddress(getHelperPort(retry), family, (uint8_t *)addr.saddr, ifname);
+        MACHRETRYLOOP_END(kr, retry, err, fin);
+fin:
+        (void)err;
+    });
 }
 }
index c1f2497f71e6d1fa82b710176dbf3518ade25138..813ab52942a49057f3d46737f61e3ad2a00e9511 100644 (file)
 #include <unistd.h>
 #include <Security/Security.h>
 #include <SystemConfiguration/SystemConfiguration.h>
 #include <unistd.h>
 #include <Security/Security.h>
 #include <SystemConfiguration/SystemConfiguration.h>
-#include <SystemConfiguration/SCDynamicStore.h>
 #include <SystemConfiguration/SCPreferencesSetSpecific.h>
 #include <SystemConfiguration/SCPreferencesSetSpecific.h>
-#include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
 #include <TargetConditionals.h>
 #include <IOKit/pwr_mgt/IOPMLib.h>
 #include <net/bpf.h>
 #include <TargetConditionals.h>
 #include <IOKit/pwr_mgt/IOPMLib.h>
 #include <net/bpf.h>
+#include <sys/sysctl.h>
 
 #include "mDNSEmbeddedAPI.h"
 #include "dns_sd.h"
 
 #include "mDNSEmbeddedAPI.h"
 #include "dns_sd.h"
@@ -61,7 +60,6 @@
 
 #include <netinet/ip.h>
 #include <netinet/tcp.h>
 
 #include <netinet/ip.h>
 #include <netinet/tcp.h>
-#include <netinet6/nd6.h>
 
 #ifndef RTF_IFSCOPE
 #define RTF_IFSCOPE 0x1000000
 
 #ifndef RTF_IFSCOPE
 #define RTF_IFSCOPE 0x1000000
@@ -150,44 +148,6 @@ kern_return_t do_mDNSRequestBPF(__unused mach_port_t port, audit_token_t token)
     return KERN_SUCCESS;
 }
 
     return KERN_SUCCESS;
 }
 
-kern_return_t do_mDNSInterfaceAdvtIoctl(__unused mach_port_t port, const char *ifname, int op, audit_token_t token)
-{
-    struct  in6_ndireq nd;
-    mDNSu32 newflags;
-    int     sock;
-
-    if (!authorized(&token))
-    {
-        return KERN_SUCCESS;
-    }
-
-    if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
-    {
-        helplog(ASL_LEVEL_ERR, "%s: Socket call failed - error (%d) %s", __func__, errno, strerror(errno));
-        return errno;
-    }
-    memset(&nd, 0, sizeof(nd));
-    strlcpy(nd.ifname, ifname, sizeof(nd.ifname));
-
-    if (ioctl(sock, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0)
-    {
-        helplog(ASL_LEVEL_ERR, "%s: ioctl call SIOCGIFINFO_IN6 failed - error (%d) %s", __func__, errno, strerror(errno));
-        close(sock);
-        return errno;
-    }
-    newflags = nd.ndi.flags;
-    newflags = (op == 0) ? (newflags |  ND6_IFF_IGNORE_NA) : (newflags & ~(ND6_IFF_IGNORE_NA));
-
-    if (ioctl(sock, SIOCSIFINFO_FLAGS, (caddr_t)&nd) < 0)
-    {
-        helplog(ASL_LEVEL_ERR, "%s: ioctl call SIOCSIFINFO_IN6 failed - error (%d) %s", __func__, errno, strerror(errno));
-        close(sock);
-        return errno;
-    }
-    close(sock);
-    return KERN_SUCCESS;
-}
-
 kern_return_t do_mDNSPowerRequest(__unused mach_port_t port, int key, int interval, int *err, audit_token_t token)
 {
     *err = -1;
 kern_return_t do_mDNSPowerRequest(__unused mach_port_t port, int key, int interval, int *err, audit_token_t token)
 {
     *err = -1;
@@ -393,86 +353,6 @@ kern_return_t do_mDNSNotify(__unused mach_port_t port, const char *title, const
     return KERN_SUCCESS;
 }
 
     return KERN_SUCCESS;
 }
 
-kern_return_t
-do_mDNSDynamicStoreSetConfig(__unused mach_port_t port, int key,
-                             const char* subkey, vm_offset_t value, mach_msg_type_number_t valueCnt,
-                             audit_token_t token)
-{
-    CFStringRef sckey = NULL;
-    Boolean release_sckey = FALSE;
-    CFDataRef bytes = NULL;
-    CFPropertyListRef plist = NULL;
-    SCDynamicStoreRef store = NULL;
-
-    debug("entry");
-    if (!authorized(&token)) goto fin;
-
-    switch ((enum mDNSDynamicStoreSetConfigKey)key)
-    {
-    case kmDNSMulticastConfig:
-        sckey = CFSTR("State:/Network/" kDNSServiceCompMulticastDNS);
-        break;
-    case kmDNSDynamicConfig:
-        sckey = CFSTR("State:/Network/DynamicDNS");
-        break;
-    case kmDNSPrivateConfig:
-        sckey = CFSTR("State:/Network/" kDNSServiceCompPrivateDNS);
-        break;
-    case kmDNSBackToMyMacConfig:
-        sckey = CFSTR("State:/Network/BackToMyMac");
-        break;
-    case kmDNSSleepProxyServersState:
-    {
-        CFMutableStringRef tmp = CFStringCreateMutable(kCFAllocatorDefault, 0);
-        CFStringAppend(tmp, CFSTR("State:/Network/Interface/"));
-        CFStringAppendCString(tmp, subkey, kCFStringEncodingUTF8);
-        CFStringAppend(tmp, CFSTR("/SleepProxyServers"));
-        sckey = CFStringCreateCopy(kCFAllocatorDefault, tmp);
-        release_sckey = TRUE;
-        CFRelease(tmp);
-        break;
-    }
-    default:
-        debug("unrecognized key %d", key);
-        goto fin;
-    }
-    if (NULL == (bytes = CFDataCreateWithBytesNoCopy(NULL, (void *)value,
-                                                     valueCnt, kCFAllocatorNull)))
-    {
-        debug("CFDataCreateWithBytesNoCopy of value failed");
-        goto fin;
-    }
-    if (NULL == (plist = CFPropertyListCreateFromXMLData(NULL, bytes,
-                                                         kCFPropertyListImmutable, NULL)))
-    {
-        debug("CFPropertyListCreateFromXMLData of bytes failed");
-        goto fin;
-    }
-    CFRelease(bytes);
-    bytes = NULL;
-    if (NULL == (store = SCDynamicStoreCreate(NULL,
-                                              CFSTR(kmDNSHelperServiceName), NULL, NULL)))
-    {
-        debug("SCDynamicStoreCreate failed: %s", SCErrorString(SCError()));
-        goto fin;
-    }
-    SCDynamicStoreSetValue(store, sckey, plist);
-    debug("succeeded");
-
-fin:
-    if (NULL != bytes)
-        CFRelease(bytes);
-    if (NULL != plist)
-        CFRelease(plist);
-    if (NULL != store)
-        CFRelease(store);
-    if (release_sckey && sckey)
-        CFRelease(sckey);
-    vm_deallocate(mach_task_self(), value, valueCnt);
-    update_idle_timer();
-    return KERN_SUCCESS;
-}
-
 char usercompname[MAX_DOMAIN_LABEL+1] = {0}; // the last computer name the user saw
 char userhostname[MAX_DOMAIN_LABEL+1] = {0}; // the last local host name the user saw
 char lastcompname[MAX_DOMAIN_LABEL+1] = {0}; // the last computer name saved to preferences
 char usercompname[MAX_DOMAIN_LABEL+1] = {0}; // the last computer name the user saw
 char userhostname[MAX_DOMAIN_LABEL+1] = {0}; // the last local host name the user saw
 char lastcompname[MAX_DOMAIN_LABEL+1] = {0}; // the last computer name saved to preferences
@@ -1365,6 +1245,13 @@ createAnonymousRacoonConfiguration(const char *fqdn)
         "  proposal_check claim;\n"
         "  proposal {\n"
         "    encryption_algorithm aes;\n"
         "  proposal_check claim;\n"
         "  proposal {\n"
         "    encryption_algorithm aes;\n"
+        "    hash_algorithm sha256;\n"
+        "    authentication_method pre_shared_key;\n"
+        "    dh_group 2;\n"
+        "    lifetime time 15 min;\n"
+        "  }\n"
+        "  proposal {\n"
+        "    encryption_algorithm aes;\n"
         "    hash_algorithm sha1;\n"
         "    authentication_method pre_shared_key;\n"
         "    dh_group 2;\n"
         "    hash_algorithm sha1;\n"
         "    authentication_method pre_shared_key;\n"
         "    dh_group 2;\n"
@@ -1375,7 +1262,7 @@ createAnonymousRacoonConfiguration(const char *fqdn)
         "  pfs_group 2;\n"
         "  lifetime time 10 min;\n"
         "  encryption_algorithm aes;\n"
         "  pfs_group 2;\n"
         "  lifetime time 10 min;\n"
         "  encryption_algorithm aes;\n"
-        "  authentication_algorithm hmac_sha1;\n"
+        "  authentication_algorithm hmac_sha256,hmac_sha1;\n"
         "  compression_algorithm deflate;\n"
         "}\n";
     char tmp_config_path[64];
         "  compression_algorithm deflate;\n"
         "}\n";
     char tmp_config_path[64];
@@ -1591,7 +1478,7 @@ startRacoon(void)
     }
 
     u_int32_t btmm_cookie = 0x4d4d5442;
     }
 
     u_int32_t btmm_cookie = 0x4d4d5442;
-    vpnctl_hdr h = { VPNCTL_CMD_PING, 0, btmm_cookie, 0, 0, 0 };
+    vpnctl_hdr h = { htons(VPNCTL_CMD_PING), 0, btmm_cookie, 0, 0, 0 };
     size_t bytes = 0;
     ssize_t ret = 0;
 
     size_t bytes = 0;
     ssize_t ret = 0;
 
@@ -2114,6 +2001,13 @@ do_mDNSAutoTunnelSetKeys(__unused mach_port_t port, int replacedelete,
         "  proposal_check claim;\n"
         "  proposal {\n"
         "    encryption_algorithm aes;\n"
         "  proposal_check claim;\n"
         "  proposal {\n"
         "    encryption_algorithm aes;\n"
+        "    hash_algorithm sha256;\n"
+        "    authentication_method pre_shared_key;\n"
+        "    dh_group 2;\n"
+        "    lifetime time 15 min;\n"
+        "  }\n"
+        "  proposal {\n"
+        "    encryption_algorithm aes;\n"
         "    hash_algorithm sha1;\n"
         "    authentication_method pre_shared_key;\n"
         "    dh_group 2;\n"
         "    hash_algorithm sha1;\n"
         "    authentication_method pre_shared_key;\n"
         "    dh_group 2;\n"
@@ -2124,14 +2018,14 @@ do_mDNSAutoTunnelSetKeys(__unused mach_port_t port, int replacedelete,
         "  pfs_group 2;\n"
         "  lifetime time 10 min;\n"
         "  encryption_algorithm aes;\n"
         "  pfs_group 2;\n"
         "  lifetime time 10 min;\n"
         "  encryption_algorithm aes;\n"
-        "  authentication_algorithm hmac_sha1;\n"
+        "  authentication_algorithm hmac_sha256,hmac_sha1;\n"
         "  compression_algorithm deflate;\n"
         "}\n\n"
         "sainfo address %s any address %s any {\n"
         "  pfs_group 2;\n"
         "  lifetime time 10 min;\n"
         "  encryption_algorithm aes;\n"
         "  compression_algorithm deflate;\n"
         "}\n\n"
         "sainfo address %s any address %s any {\n"
         "  pfs_group 2;\n"
         "  lifetime time 10 min;\n"
         "  encryption_algorithm aes;\n"
-        "  authentication_algorithm hmac_sha1;\n"
+        "  authentication_algorithm hmac_sha256,hmac_sha1;\n"
         "  compression_algorithm deflate;\n"
         "}\n";
     char path[PATH_MAX] = "";
         "  compression_algorithm deflate;\n"
         "}\n";
     char path[PATH_MAX] = "";
@@ -2683,3 +2577,268 @@ again:
     close(sock);
     return KERN_SUCCESS;
 }
     close(sock);
     return KERN_SUCCESS;
 }
+
+
+kern_return_t do_mDNSRetrieveTCPInfo(__unused mach_port_t port, int family, v6addr_t laddr, uint16_t lport, v6addr_t raddr, uint16_t  rport,
+                                     uint32_t *seq, uint32_t *ack, uint16_t *win, int32_t *intfid, audit_token_t token)
+{
+    struct tcp_info   ti;
+    struct info_tuple itpl;
+    int               mib[4];
+    unsigned int      miblen;
+    size_t            len;
+    size_t            sz;
+
+    memset(&itpl, 0, sizeof(struct info_tuple));
+    memset(&ti,   0, sizeof(struct tcp_info));
+
+    if (!authorized(&token))
+    {
+        helplog(ASL_LEVEL_ERR, "mDNSRetrieveTCPInfo: Not authorized");
+        return kmDNSHelperNotAuthorized;
+    }
+
+    if (family == AF_INET)
+    {
+        memcpy(&itpl.itpl_local_sin.sin_addr,  laddr, sizeof(struct in_addr));
+        memcpy(&itpl.itpl_remote_sin.sin_addr, raddr, sizeof(struct in_addr));
+        itpl.itpl_local_sin.sin_port    = lport;
+        itpl.itpl_remote_sin.sin_port   = rport;
+        itpl.itpl_local_sin.sin_family  = AF_INET;
+        itpl.itpl_remote_sin.sin_family = AF_INET;
+    }
+    else
+    {
+        memcpy(&itpl.itpl_local_sin6.sin6_addr,  laddr, sizeof(struct in6_addr));
+        memcpy(&itpl.itpl_remote_sin6.sin6_addr, raddr, sizeof(struct in6_addr));
+        itpl.itpl_local_sin6.sin6_port    = lport;
+        itpl.itpl_remote_sin6.sin6_port   = rport;
+        itpl.itpl_local_sin6.sin6_family  = AF_INET6;
+        itpl.itpl_remote_sin6.sin6_family = AF_INET6;
+    }
+    itpl.itpl_proto = IPPROTO_TCP;
+    sz = sizeof(mib)/sizeof(mib[0]);
+    if (sysctlnametomib("net.inet.tcp.info", mib, &sz) == -1)
+    {
+        helplog(ASL_LEVEL_ERR, "do_RetrieveTCPInfo: sysctlnametomib failed %d, %s", errno, strerror(errno));
+        return errno;
+    }
+    miblen = (unsigned int)sz;
+    len    = sizeof(struct tcp_info);
+    if (sysctl(mib, miblen, &ti, &len, &itpl, sizeof(struct info_tuple)) == -1)
+    {
+        helplog(ASL_LEVEL_ERR, "do_RetrieveTCPInfo: sysctl failed %d, %s", errno, strerror(errno));
+        return errno;
+    }
+
+    *seq    = ti.tcpi_snd_nxt - 1;
+    *ack    = ti.tcpi_rcv_nxt;
+    *win    = ti.tcpi_rcv_space >> ti.tcpi_rcv_wscale;
+    *intfid = ti.tcpi_last_outif;
+    return KERN_SUCCESS;
+}
+
+static int getMACAddress(int family, v6addr_t raddr, v6addr_t gaddr, int *gfamily, ethaddr_t eth)
+{
+    struct
+    {
+        struct rt_msghdr m_rtm;
+        char   m_space[512];
+    } m_rtmsg;
+
+    struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
+    char  *cp  = m_rtmsg.m_space;
+    int    seq = 6367, sock, rlen, i;
+    struct sockaddr_in      *sin  = NULL;
+    struct sockaddr_in6     *sin6 = NULL;
+    struct sockaddr_dl      *sdl  = NULL;
+    struct sockaddr_storage  sins;
+    struct sockaddr_dl       sdl_m;
+
+#define NEXTADDR(w, s, len)         \
+    if (rtm->rtm_addrs & (w))       \
+    {                               \
+        bcopy((char *)s, cp, len);  \
+        cp += len;                  \
+    }
+
+    bzero(&sins,  sizeof(struct sockaddr_storage));
+    bzero(&sdl_m, sizeof(struct sockaddr_dl));
+    bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
+
+    sock = socket(PF_ROUTE, SOCK_RAW, 0);
+    if (sock < 0)
+    {
+        helplog(ASL_LEVEL_ERR, "mDNSGetRemoteMAC: Can not open the socket - %s", strerror(errno));
+        return errno;
+    }
+
+    rtm->rtm_addrs   |= RTA_DST | RTA_GATEWAY;
+    rtm->rtm_type     = RTM_GET;
+    rtm->rtm_flags    = 0;
+    rtm->rtm_version  = RTM_VERSION;
+    rtm->rtm_seq      = ++seq;
+
+    sdl_m.sdl_len     = sizeof(sdl_m);
+    sdl_m.sdl_family  = AF_LINK;
+    if (family == AF_INET)
+    {
+        sin = (struct sockaddr_in*)&sins;
+        sin->sin_family = AF_INET;
+        sin->sin_len    = sizeof(struct sockaddr_in);
+        memcpy(&sin->sin_addr, raddr, sizeof(struct in_addr));
+        NEXTADDR(RTA_DST, sin, sin->sin_len);
+    }
+    else if (family == AF_INET6)
+    {
+        sin6 = (struct sockaddr_in6 *)&sins;
+        sin6->sin6_len    = sizeof(struct sockaddr_in6);
+        sin6->sin6_family = AF_INET6;
+        memcpy(&sin6->sin6_addr, raddr, sizeof(struct in6_addr));
+        NEXTADDR(RTA_DST, sin6, sin6->sin6_len);
+    }
+    NEXTADDR(RTA_GATEWAY, &sdl_m, sdl_m.sdl_len);
+    rtm->rtm_msglen = rlen = cp - (char *)&m_rtmsg;
+
+    if (write(sock, (char *)&m_rtmsg, rlen) < 0)
+    {
+        helplog(ASL_LEVEL_INFO, "do_mDNSGetRemoteMAC: writing to routing socket: %s", strerror(errno));
+        close(sock);
+        return errno;
+    }
+
+    do
+    {
+        rlen = read(sock, (char *)&m_rtmsg, sizeof(m_rtmsg));
+    }
+    while (rlen > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != getpid()));
+
+    if (rlen < 0)
+        helplog(ASL_LEVEL_ERR, "do_mDNSGetRemoteMAC: Read from routing socket failed");
+
+    if (family == AF_INET)
+    {
+        sin = (struct sockaddr_in *) (rtm + 1);
+        sdl = (struct sockaddr_dl *) (sin->sin_len + (char *) sin);
+    }
+    else if (family == AF_INET6)
+    {
+        sin6 = (struct sockaddr_in6 *) (rtm +1);
+        sdl  = (struct sockaddr_dl  *) (sin6->sin6_len + (char *) sin6);
+    }
+    // If the address is not on the local net, we get the IP address of the gateway.
+    // We would have to repeat the process to get the MAC address of the gateway
+    *gfamily = sdl->sdl_family;
+    if (sdl->sdl_family == AF_INET)
+    {
+        struct sockaddr_in *new_sin = (struct sockaddr_in *)(sin->sin_len +(char*) sin);
+        memcpy(gaddr, &new_sin->sin_addr, sizeof(struct in_addr));
+        close(sock);
+        return -1;
+    }
+    else if (sdl->sdl_family == AF_INET6)
+    {
+        struct sockaddr_in6 *new_sin6 = (struct sockaddr_in6 *)(sin6->sin6_len +(char*) sin6);
+        memcpy(gaddr, &new_sin6->sin6_addr, sizeof(struct in6_addr));
+        close(sock);
+        return -1;
+    }
+
+    unsigned char *ptr = (unsigned char *)LLADDR(sdl);
+    for (i = 0; i < ETHER_ADDR_LEN; i++)
+        (eth)[i] = *(ptr +i);
+
+    close(sock);
+    return KERN_SUCCESS;
+}
+
+kern_return_t do_mDNSGetRemoteMAC(__unused mach_port_t port, int family, v6addr_t raddr, ethaddr_t eth, audit_token_t token)
+{
+    int      ret = 0;
+    v6addr_t gateway;
+    int      gfamily;
+    int      count = 0;
+
+    if (!authorized(&token))
+    {
+        helplog(ASL_LEVEL_ERR, "mDNSGetRemoteMAC: Not authorized");
+        return kmDNSHelperNotAuthorized;
+    }
+
+    do
+    {
+        ret = getMACAddress(family, raddr, gateway, &gfamily, eth);
+        if (ret == -1)
+        {
+            memcpy(raddr, gateway, sizeof(family));
+            family = gfamily;
+            count++;
+        }
+    }
+    while ((ret == -1) && (count < 5));
+    return ret;
+}
+
+
+kern_return_t do_mDNSStoreSPSMACAddress(__unused mach_port_t port, int family, v6addr_t spsaddr, const char *ifname, audit_token_t token)
+{
+    ethaddr_t              eth;
+    char                   spsip[INET6_ADDRSTRLEN];
+    int                    ret   = 0;
+    CFStringRef            sckey = NULL;
+    SCDynamicStoreRef      store = NULL;
+    CFMutableDictionaryRef dict  = NULL;
+
+    if (!authorized(&token))
+    {
+        helplog(ASL_LEVEL_ERR, "mDNSStoreSPSMAC: Not authorized");
+        return kmDNSHelperNotAuthorized;
+    }
+
+    // Get the MAC address of the Sleep Proxy Server
+    memset(eth, 0, sizeof(eth));
+    ret = do_mDNSGetRemoteMAC(port, family, spsaddr, eth, token);
+    if (ret !=  KERN_SUCCESS)
+    {
+        helplog(ASL_LEVEL_ERR, "mDNSStoreSPSMAC: Failed to determine the MAC address");
+        goto fin;
+    }
+
+    // Create/Update the dynamic store entry for the specified interface
+    sckey = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s%s%s"), "State:/Network/Interface/", ifname, "/BonjourSleepProxyAddress");
+    dict  = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+    if (!dict)
+    {
+        helplog(ASL_LEVEL_ERR, "SPSCreateDict: Could not create CFDictionary dict");
+        ret = KERN_FAILURE;
+        goto fin;
+    }
+
+    CFStringRef macaddr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%02x:%02x:%02x:%02x:%02x:%02x"), eth[0], eth[1], eth[2], eth[3], eth[4], eth[5]);
+    CFDictionarySetValue(dict, CFSTR("MACAddress"), macaddr);
+    CFRelease(macaddr);
+
+    if( NULL == inet_ntop(family, (void *)spsaddr, spsip, sizeof(spsip)))
+    {
+         helplog(ASL_LEVEL_ERR, "inet_ntop failed: %s", strerror(errno));
+         ret = kmDNSHelperInvalidNetworkAddress;
+         goto fin;
+    }
+
+    CFStringRef ipaddr = CFStringCreateWithCString(NULL, spsip, kCFStringEncodingUTF8);
+    CFDictionarySetValue(dict, CFSTR("IPAddress"), ipaddr);
+    CFRelease(ipaddr);
+
+    SCDynamicStoreSetValue(store, sckey, dict);
+
+fin:
+    if (NULL != store)
+        CFRelease(store);
+    if (NULL != sckey)
+        CFRelease(sckey);
+    if (NULL != dict)
+        CFRelease(dict);
+
+    update_idle_timer();
+    return ret;
+}
index c7dabc55c8ce967b43a42b65e8eb0e8d41b50cd4..a2982372f0d3b67a4355a8bbef1b7b0a28e3be72 100644 (file)
 
 #define kmDNSHelperServiceName "com.apple.mDNSResponderHelper"
 
 
 #define kmDNSHelperServiceName "com.apple.mDNSResponderHelper"
 
-enum mDNSDynamicStoreSetConfigKey
-{
-    kmDNSMulticastConfig = 1,
-    kmDNSDynamicConfig,
-    kmDNSPrivateConfig,
-    kmDNSBackToMyMacConfig,
-    kmDNSSleepProxyServersState
-};
-
 enum mDNSPreferencesSetNameKey
 {
     kmDNSComputerName = 1,
 enum mDNSPreferencesSetNameKey
 {
     kmDNSComputerName = 1,
@@ -72,11 +63,11 @@ enum mDNSHelperErrors
 
 extern const char *mDNSHelperError(int errornum);
 
 
 extern const char *mDNSHelperError(int errornum);
 
+extern mStatus mDNSHelperInit(void);
 extern void mDNSRequestBPF(void);
 extern int  mDNSPowerRequest(int key, int interval);
 extern int  mDNSSetLocalAddressCacheEntry(int ifindex, int family, const v6addr_t ip, const ethaddr_t eth);
 extern void mDNSNotify(const char *title, const char *msg);     // Both strings are UTF-8 text
 extern void mDNSRequestBPF(void);
 extern int  mDNSPowerRequest(int key, int interval);
 extern int  mDNSSetLocalAddressCacheEntry(int ifindex, int family, const v6addr_t ip, const ethaddr_t eth);
 extern void mDNSNotify(const char *title, const char *msg);     // Both strings are UTF-8 text
-extern void mDNSDynamicStoreSetConfig(int key, const char *subkey, CFPropertyListRef value);
 extern void mDNSPreferencesSetName(int key, domainlabel *old, domainlabel *new);
 extern int  mDNSKeychainGetSecrets(CFArrayRef *secrets);
 extern void mDNSConfigureServer(int updown, const char *const prefix, const domainname *const fqdn);
 extern void mDNSPreferencesSetName(int key, domainlabel *old, domainlabel *new);
 extern int  mDNSKeychainGetSecrets(CFArrayRef *secrets);
 extern void mDNSConfigureServer(int updown, const char *const prefix, const domainname *const fqdn);
@@ -85,7 +76,9 @@ extern int  mDNSAutoTunnelSetKeys(int replacedelete, v6addr_t local_inner,
                                   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, uint32_t count, pfArray_t portArray, pfArray_t protocolArray);
                                   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, uint32_t count, pfArray_t portArray, pfArray_t protocolArray);
-extern int mDNSSendKeepalive(v6addr_t sadd, v6addr_t dadd, uint16_t lport, uint16_t rport, unsigned seq, unsigned ack, uint16_t win);
-extern int mDNSInterfaceAdvtIoctl(const char *ifname, int op);
+extern void mDNSSendKeepalive(v6addr_t sadd, v6addr_t dadd, uint16_t lport, uint16_t rport, unsigned seq, unsigned ack, uint16_t win);
+extern int  mDNSRetrieveTCPInfo(int family, v6addr_t laddr, uint16_t lport, v6addr_t raddr, uint16_t rport, uint32_t *seq, uint32_t *ack, uint16_t *win, int32_t *intfid);
+extern void mDNSGetRemoteMAC(mDNS *const m, int family, v6addr_t raddr);
+extern void mDNSStoreSPSMACAddress(int family, v6addr_t spsaddr, char *ifname);
 
 #endif /* H_HELPER_H */
 
 #endif /* H_HELPER_H */
index 02c8af484c3d4bd82d9d84d584874c003fcb5fc9..58363082b99fb6d9aa777c098c39c68c248cfec2 100644 (file)
@@ -58,13 +58,6 @@ simpleroutine mDNSNotify(            port                    : mach_port_t;
                                                                msg                             : string_t;
                ServerAuditToken                token                   : audit_token_t);
 
                                                                msg                             : string_t;
                ServerAuditToken                token                   : audit_token_t);
 
-simpleroutine mDNSDynamicStoreSetConfig(
-                                                               port                    : mach_port_t;
-                                                               key                             : int;
-                                                               subkey                  : string_t;
-                                                               value                   : pointer_t;
-               ServerAuditToken                token                   : audit_token_t);
-
 simpleroutine mDNSPreferencesSetName(
                                                                port                    : mach_port_t;
                                                                key                             : int;
 simpleroutine mDNSPreferencesSetName(
                                                                port                    : mach_port_t;
                                                                key                             : int;
@@ -124,7 +117,27 @@ simpleroutine mDNSSendKeepalive( port              : mach_port_t;
                                                                win                             : uint16_t;
                ServerAuditToken                token                   : audit_token_t);
 
                                                                win                             : uint16_t;
                ServerAuditToken                token                   : audit_token_t);
 
-simpleroutine mDNSInterfaceAdvtIoctl( port             : mach_port_t;
-                                                                       ifname          : string_t;
-                                                                       op                      : int;
-                                       ServerAuditToken token          : audit_token_t);
+routine mDNSRetrieveTCPInfo(
+                                                               port                    : mach_port_t;
+                                                               family                  : int;
+                                                               laddr                   : v6addr_t;
+                                                               lport                   : uint16_t;
+                                                               raddr                   : v6addr_t;
+                                                               rport                   : uint16_t;
+                                                out    seq                             : uint32_t;
+                                                out    ack                             : uint32_t;
+                                                out    win                             : uint16_t;
+                                                out    intfid                  : int32_t;
+               ServerAuditToken                token                   : audit_token_t);
+
+routine mDNSGetRemoteMAC(              port                    : mach_port_t;
+                                                               family                  : int;
+                                                               raddr                   : v6addr_t;
+               out                                             eth                             : ethaddr_t;
+               ServerAuditToken                token                   : audit_token_t);
+
+simpleroutine mDNSStoreSPSMACAddress(  port                    : mach_port_t;
+                                                               family                  : int;
+                                                               spsaddr                 : v6addr_t;
+                                                               ifname                  : string_t;
+               ServerAuditToken                token                   : audit_token_t);
index e48ce4851728d3b1b1e32f2d818a5980552db358..3c42ad3bd9b2aedb55c671a1d9193e047511d1e7 100644 (file)
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 4 -*-
  *
 /* -*- Mode: C; tab-width: 4 -*-
  *
- * Copyright (c) 2002-2012 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2002-2013 Apple Computer, 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.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 // including ones that mDNSResponder chooses not to use.
 #define LIST_ALL_INTERFACES 0
 
 // including ones that mDNSResponder chooses not to use.
 #define LIST_ALL_INTERFACES 0
 
-// For enabling AAAA records over IPv4. Setting this to 0 sends only
-// A records over IPv4 and AAAA over IPv6. Setting this to 1 sends both
-// AAAA and A records over both IPv4 and IPv6.
-#define AAAA_OVER_V4 1
-
-// In Mac OS X 10.4 and earlier, to reduce traffic, we would send and receive using IPv6 only on interfaces that had no routable
-// IPv4 address. Having a routable IPv4 address assigned is a reasonable indicator of being on a large configured network,
-// which means there's a good chance that most or all the other devices on that network should also have IPv4.
-// By doing this we lost the ability to talk to true IPv6-only devices on that link, but we cut the packet rate in half.
-// At that time, reducing the packet rate was more important than v6-only devices on a large configured network,
-// so were willing to make that sacrifice.
-// In Mac OS X 10.5, in 2007, two things have changed:
-// 1. IPv6-only devices are starting to become more common, so we can't ignore them.
-// 2. Other efficiency improvements in the code mean that crude hacks like this should no longer be necessary.
-
-#define USE_V6_ONLY_WHEN_NO_ROUTABLE_V4 0
-
 #include "mDNSEmbeddedAPI.h"        // Defines the interface provided to the client layer above
 #include "DNSCommon.h"
 #include "uDNS.h"
 #include "mDNSEmbeddedAPI.h"        // Defines the interface provided to the client layer above
 #include "DNSCommon.h"
 #include "uDNS.h"
@@ -49,7 +32,6 @@
 #include "PlatformCommon.h"
 #include "uds_daemon.h"
 #include "CryptoSupport.h"
 #include "PlatformCommon.h"
 #include "uds_daemon.h"
 #include "CryptoSupport.h"
-#include "MobileInternetSharing_priv.h"
 
 #include <stdio.h>
 #include <stdarg.h>                 // For va_list support
 
 #include <stdio.h>
 #include <stdarg.h>                 // For va_list support
@@ -70,7 +52,7 @@
 #include <pthread.h>
 #include <netdb.h>                  // for getaddrinfo
 #include <sys/sockio.h>             // for SIOCGIFEFLAGS
 #include <pthread.h>
 #include <netdb.h>                  // for getaddrinfo
 #include <sys/sockio.h>             // for SIOCGIFEFLAGS
-
+#include <notify.h>
 #include <netinet/in.h>             // For IP_RECVTTL
 #ifndef IP_RECVTTL
 #define IP_RECVTTL 24               // bool; receive reception TTL w/dgram
 #include <netinet/in.h>             // For IP_RECVTTL
 #ifndef IP_RECVTTL
 #define IP_RECVTTL 24               // bool; receive reception TTL w/dgram
 
 #include <netinet/tcp.h>
 
 
 #include <netinet/tcp.h>
 
-#if TARGET_OS_EMBEDDED
-#define NO_SECURITYFRAMEWORK 1
-#define NO_CFUSERNOTIFICATION 1
-#endif
-
-#ifndef NO_SECURITYFRAMEWORK
-#include <Security/SecureTransport.h>
-#include <Security/Security.h>
-#endif /* NO_SECURITYFRAMEWORK */
-
 #include <DebugServices.h>
 #include "dnsinfo.h"
 
 #include <DebugServices.h>
 #include "dnsinfo.h"
 
 #include <IOKit/IOKitLib.h>
 #include <IOKit/IOMessage.h>
 
 #include <IOKit/IOKitLib.h>
 #include <IOKit/IOMessage.h>
 
-#if USE_IOPMCOPYACTIVEPMPREFERENCES
 #include <IOKit/ps/IOPowerSources.h>
 #include <IOKit/ps/IOPowerSourcesPrivate.h>
 #include <IOKit/ps/IOPowerSources.h>
 #include <IOKit/ps/IOPowerSourcesPrivate.h>
-#endif
+#include <IOKit/ps/IOPSKeys.h>
 
 #include <mach/mach_error.h>
 #include <mach/mach_port.h>
 
 #include <mach/mach_error.h>
 #include <mach/mach_port.h>
 #include "P2PPacketFilter.h"
 
 #include <asl.h>
 #include "P2PPacketFilter.h"
 
 #include <asl.h>
-
-#if DNSINFO_VERSION >= 20110420
 #include <SystemConfiguration/SCPrivate.h>
 #include <SystemConfiguration/SCPrivate.h>
-#else
-#include <SystemConfiguration/SCDynamicStorePrivate.h>
-#endif // DNSINFO_VERSION >= 20110420
 
 // Include definition of opaque_presence_indication for KEV_DL_NODE_PRESENCE handling logic.
 #include <Kernel/IOKit/apple80211/apple80211_var.h>
 
 // Include definition of opaque_presence_indication for KEV_DL_NODE_PRESENCE handling logic.
 #include <Kernel/IOKit/apple80211/apple80211_var.h>
 #if APPLE_OSX_mDNSResponder
 #include <DeviceToDeviceManager/DeviceToDeviceManager.h>
 #include <AWACS.h>
 #if APPLE_OSX_mDNSResponder
 #include <DeviceToDeviceManager/DeviceToDeviceManager.h>
 #include <AWACS.h>
-
 #if !NO_D2D
 D2DStatus D2DInitialize(CFRunLoopRef runLoop, D2DServiceCallback serviceCallback, void* userData) __attribute__((weak_import));
 #if !NO_D2D
 D2DStatus D2DInitialize(CFRunLoopRef runLoop, D2DServiceCallback serviceCallback, void* userData) __attribute__((weak_import));
-D2DStatus D2DTerminate() __attribute__((weak_import));
-D2DStatus D2DStartAdvertisingPair(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize) __attribute__((weak_import));
-D2DStatus D2DStopAdvertisingPair(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize) __attribute__((weak_import));
-D2DStatus D2DStartBrowsingForKey(const Byte *key, const size_t keySize) __attribute__((weak_import));
-D2DStatus D2DStopBrowsingForKey(const Byte *key, const size_t keySize) __attribute__((weak_import));
-void D2DStartResolvingPair(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize) __attribute__((weak_import));
-void D2DStopResolvingPair(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize) __attribute__((weak_import));
 D2DStatus D2DRetain(D2DServiceInstance instanceHandle, D2DTransportType transportType) __attribute__((weak_import));
 D2DStatus D2DRetain(D2DServiceInstance instanceHandle, D2DTransportType transportType) __attribute__((weak_import));
+D2DStatus D2DStopAdvertisingPairOnTransport(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize, D2DTransportType transport) __attribute__((weak_import));
 D2DStatus D2DRelease(D2DServiceInstance instanceHandle, D2DTransportType transportType) __attribute__((weak_import));
 D2DStatus D2DRelease(D2DServiceInstance instanceHandle, D2DTransportType transportType) __attribute__((weak_import));
+D2DStatus D2DStartAdvertisingPairOnTransport(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize, D2DTransportType transport) __attribute__((weak_import));
+D2DStatus D2DStartBrowsingForKeyOnTransport(const Byte *key, const size_t keySize, D2DTransportType transport) __attribute__((weak_import));
+D2DStatus D2DStopBrowsingForKeyOnTransport(const Byte *key, const size_t keySize, D2DTransportType transport) __attribute__((weak_import));
+void D2DStartResolvingPairOnTransport(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize, D2DTransportType transport) __attribute__((weak_import));
+void D2DStopResolvingPairOnTransport(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize, D2DTransportType transport) __attribute__((weak_import));
+D2DStatus D2DTerminate() __attribute__((weak_import));
 
 #endif // ! NO_D2D
 
 
 #endif // ! NO_D2D
 
@@ -146,8 +111,18 @@ D2DStatus D2DRelease(D2DServiceInstance instanceHandle, D2DTransportType transpo
 #define NO_AWACS 1
 #endif // APPLE_OSX_mDNSResponder
 
 #define NO_AWACS 1
 #endif // APPLE_OSX_mDNSResponder
 
+#if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED
+#include <IOKit/platform/IOPlatformSupportPrivate.h>
+#endif // APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED
+
+
 #define kInterfaceSpecificOption "interface="
 
 #define kInterfaceSpecificOption "interface="
 
+#define mDNS_IOREG_KEY               "mDNS_KEY"
+#define mDNS_IOREG_VALUE             "2009-07-30"
+#define mDNS_IOREG_KA_KEY            "mDNS_Keepalive"
+#define mDNS_USER_CLIENT_CREATE_TYPE 'mDNS'
+
 // cache the InterfaceID of the AWDL interface 
 static mDNSInterfaceID AWDLInterfaceID;
 
 // cache the InterfaceID of the AWDL interface 
 static mDNSInterfaceID AWDLInterfaceID;
 
@@ -179,11 +154,11 @@ static CFStringRef NetworkChangedKey_IPv6;
 static CFStringRef NetworkChangedKey_Hostnames;
 static CFStringRef NetworkChangedKey_Computername;
 static CFStringRef NetworkChangedKey_DNS;
 static CFStringRef NetworkChangedKey_Hostnames;
 static CFStringRef NetworkChangedKey_Computername;
 static CFStringRef NetworkChangedKey_DNS;
+static CFStringRef NetworkChangedKey_StateInterfacePrefix;
 static CFStringRef NetworkChangedKey_DynamicDNS       = CFSTR("Setup:/Network/DynamicDNS");
 static CFStringRef NetworkChangedKey_BackToMyMac      = CFSTR("Setup:/Network/BackToMyMac");
 static CFStringRef NetworkChangedKey_BTMMConnectivity = CFSTR("State:/Network/Connectivity");
 static CFStringRef NetworkChangedKey_PowerSettings    = CFSTR("State:/IOKit/PowerManagement/CurrentSettings");
 static CFStringRef NetworkChangedKey_DynamicDNS       = CFSTR("Setup:/Network/DynamicDNS");
 static CFStringRef NetworkChangedKey_BackToMyMac      = CFSTR("Setup:/Network/BackToMyMac");
 static CFStringRef NetworkChangedKey_BTMMConnectivity = CFSTR("State:/Network/Connectivity");
 static CFStringRef NetworkChangedKey_PowerSettings    = CFSTR("State:/IOKit/PowerManagement/CurrentSettings");
-static CFStringRef NetworkChangedKey_InternetSharing  = CFSTR("com.apple.InternetSharing");
 
 static char HINFO_HWstring_buffer[32];
 static char *HINFO_HWstring = "Device";
 
 static char HINFO_HWstring_buffer[32];
 static char *HINFO_HWstring = "Device";
@@ -191,8 +166,13 @@ static int HINFO_HWstring_prefixlen = 6;
 
 mDNSexport int WatchDogReportingThreshold = 250;
 
 
 mDNSexport int WatchDogReportingThreshold = 250;
 
-#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 dispatch_queue_t SSLqueue;
 dispatch_queue_t SSLqueue;
+
+//To prevent blocking the main queue, all writes to DynamicStore happen on the DynamicStoreQueue
+static dispatch_queue_t DynamicStoreQueue;
+
+#if TARGET_OS_EMBEDDED
+#define kmDNSResponderManagedPrefsID CFSTR("/Library/Managed Preferences/mobile/com.apple.mDNSResponder.plist")
 #endif
 
 #if APPLE_OSX_mDNSResponder
 #endif
 
 #if APPLE_OSX_mDNSResponder
@@ -205,10 +185,16 @@ mDNSexport int ActiveDirectoryPrimaryDomainLabelCount;
 mDNSexport mDNSAddr ActiveDirectoryPrimaryDomainServer;
 #endif // APPLE_OSX_mDNSResponder
 
 mDNSexport mDNSAddr ActiveDirectoryPrimaryDomainServer;
 #endif // APPLE_OSX_mDNSResponder
 
+// Don't send triggers too often. We arbitrarily limit it to three minutes.
+#define DNS_TRIGGER_INTERVAL (180 * mDNSPlatformOneSecond)
+
 // Used by AutoTunnel
 const char btmmprefix[] = "btmmdns:";
 const char dnsprefix[] = "dns:";
 
 // Used by AutoTunnel
 const char btmmprefix[] = "btmmdns:";
 const char dnsprefix[] = "dns:";
 
+// String Array used to write list of private domains to Dynamic Store
+static CFArrayRef privateDnsArray = NULL;
+
 // ***************************************************************************
 #if COMPILER_LIKES_PRAGMA_MARK
 #pragma mark -
 // ***************************************************************************
 #if COMPILER_LIKES_PRAGMA_MARK
 #pragma mark -
@@ -217,6 +203,30 @@ const char dnsprefix[] = "dns:";
 
 #if !NO_D2D
 
 
 #if !NO_D2D
 
+mDNSexport void D2D_start_advertising_interface(NetworkInterfaceInfo *interface)
+{
+    // AWDL wants the address and reverse address PTR record communicated
+    // via the D2D interface layer.
+    if (interface->InterfaceID == AWDLInterfaceID)
+    {
+        LogInfo("D2D_start_advertising_interface: %s", interface->ifname);
+        external_start_advertising_service(&interface->RR_A.resrec, NULL);
+        external_start_advertising_service(&interface->RR_PTR.resrec, NULL);
+    }
+}
+
+mDNSexport void D2D_stop_advertising_interface(NetworkInterfaceInfo *interface)
+{
+    if (interface->InterfaceID == AWDLInterfaceID)
+    {
+        LogInfo("D2D_stop_advertising_interface: %s", interface->ifname);
+        if (interface->RR_A.resrec.RecordType)
+            external_stop_advertising_service(&interface->RR_A.resrec, NULL);
+        if (interface->RR_PTR.resrec.RecordType)
+            external_stop_advertising_service(&interface->RR_PTR.resrec, NULL);
+    }
+}
+
 // Name compression items for fake packet version number 1
 static const mDNSu8 compression_packet_v1 = 0x01;
 
 // Name compression items for fake packet version number 1
 static const mDNSu8 compression_packet_v1 = 0x01;
 
@@ -336,7 +346,7 @@ mDNSlocal mStatus DNSNameCompressionParseBytes(mDNS *const m, const mDNSu8 *cons
     return mStatus_NoError;
 }
 
     return mStatus_NoError;
 }
 
-mDNSlocal mDNSu8 * DNSNameCompressionBuildLHS(const domainname const *typeDomain, DNS_TypeValues qtype)
+mDNSlocal mDNSu8 * DNSNameCompressionBuildLHS(const domainnametypeDomain, DNS_TypeValues qtype)
 {
     mDNSu8 *ptr = putDomainNameAsLabels(&compression_base_msg, compression_lhs, compression_limit, typeDomain);
     if (!ptr) return ptr;
 {
     mDNSu8 *ptr = putDomainNameAsLabels(&compression_base_msg, compression_lhs, compression_limit, typeDomain);
     if (!ptr) return ptr;
@@ -425,17 +435,15 @@ mDNSexport void external_connection_release(const domainname *instance)
     }
 }
 
     }
 }
 
-mDNSlocal void xD2DClearCache(const domainname *regType)
+mDNSlocal void xD2DClearCache(const domainname *regType, DNS_TypeValues qtype)
 {
     D2DRecordListElem *ptr = D2DRecords;
     for ( ; ptr ; ptr = ptr->next)
     {
 {
     D2DRecordListElem *ptr = D2DRecords;
     for ( ; ptr ; ptr = ptr->next)
     {
-        if (SameDomainName(&ptr->ar.namestorage, regType))
+        if ((ptr->ar.resrec.rrtype == qtype) && SameDomainName(&ptr->ar.namestorage, regType))
         {
         {
-            char buffer[MAX_ESCAPED_DOMAIN_NAME];
             mDNS_Deregister(&mDNSStorage, &ptr->ar);
             mDNS_Deregister(&mDNSStorage, &ptr->ar);
-            ConvertDomainNameToCString(regType, buffer);
-            LogInfo("xD2DClearCache: Clearing cache record and deregistering %s", buffer);
+            LogInfo("xD2DClearCache: Clearing cache record and deregistering %s", ARDisplayString(&mDNSStorage, &ptr->ar));
         }
     }
 }
         }
     }
 }
@@ -826,7 +834,10 @@ mDNSexport void external_stop_browsing_for_service(mDNSInterfaceID InterfaceID,
             if (D2DStopBrowsingForKeyOnTransport) D2DStopBrowsingForKeyOnTransport(compression_lhs, end - compression_lhs, transportType);
         }
 
             if (D2DStopBrowsingForKeyOnTransport) D2DStopBrowsingForKeyOnTransport(compression_lhs, end - compression_lhs, transportType);
         }
 
-        xD2DClearCache(&lower);
+        // The D2D driver may not generate the D2DServiceLost event for this key after
+        // the D2DStopBrowsingForKey*() call above.  So, we flush the key from the D2D 
+        // record cache now.
+        xD2DClearCache(&lower, qtype);
     }
 }
 
     }
 }
 
@@ -913,6 +924,7 @@ mDNSexport void external_start_resolving_service(mDNSInterfaceID InterfaceID, co
     domainname lower;
     mDNSu8 *rhs = NULL;
     mDNSu8 *end = NULL;
     domainname lower;
     mDNSu8 *rhs = NULL;
     mDNSu8 *end = NULL;
+    mDNSBool AWDL_used = false;   // whether AWDL was used for this resolve
     D2DTransportType transportType, excludedTransport;
     DomainnameToLower(SkipLeadingLabels(fqdn, 1), &lower);
 
     D2DTransportType transportType, excludedTransport;
     DomainnameToLower(SkipLeadingLabels(fqdn, 1), &lower);
 
@@ -924,16 +936,34 @@ mDNSexport void external_start_resolving_service(mDNSInterfaceID InterfaceID, co
     transportType = xD2DInterfaceToTransportType(InterfaceID, flags, & excludedTransport);
     if (transportType == D2DTransportMax)
     {
     transportType = xD2DInterfaceToTransportType(InterfaceID, flags, & excludedTransport);
     if (transportType == D2DTransportMax)
     {
+        // Resolving over all the transports, except for excludedTransport if set.
         D2DTransportType i;
         for (i = 0; i < D2DTransportMax; i++)
         {
             if (i == excludedTransport) continue;
             if (D2DStartResolvingPairOnTransport) D2DStartResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, i);
         D2DTransportType i;
         for (i = 0; i < D2DTransportMax; i++)
         {
             if (i == excludedTransport) continue;
             if (D2DStartResolvingPairOnTransport) D2DStartResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, i);
+
+            if (i == D2DAWDLTransport)
+                AWDL_used = true;
         }
     }
     else
     {
         }
     }
     else
     {
+        // Resolving over one specific transport.
         if (D2DStartResolvingPairOnTransport) D2DStartResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, transportType);
         if (D2DStartResolvingPairOnTransport) D2DStartResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, transportType);
+
+        if (transportType == D2DAWDLTransport)
+            AWDL_used = true;
+    }
+
+    // AWDL wants the SRV and TXT record queries communicated over the D2D interface.
+    // We only want these records going to AWDL, so use AWDLInterfaceID as the
+    // interface and don't set any other flags.
+    if (AWDL_used && AWDLInterfaceID)
+    {
+        LogInfo("external_start_resolving_service: browse for TXT and SRV over AWDL");
+        external_start_browsing_for_service(AWDLInterfaceID, fqdn, kDNSType_TXT, NULL);
+        external_start_browsing_for_service(AWDLInterfaceID, fqdn, kDNSType_SRV, NULL);
     }
 }
 
     }
 }
 
@@ -942,6 +972,7 @@ mDNSexport void external_stop_resolving_service(mDNSInterfaceID InterfaceID, con
     domainname lower;
     mDNSu8 *rhs = NULL;
     mDNSu8 *end = NULL;
     domainname lower;
     mDNSu8 *rhs = NULL;
     mDNSu8 *end = NULL;
+    mDNSBool AWDL_used = false;   // whether AWDL was used for this resolve
     D2DTransportType transportType, excludedTransport;
     DomainnameToLower(SkipLeadingLabels(fqdn, 1), &lower);
 
     D2DTransportType transportType, excludedTransport;
     DomainnameToLower(SkipLeadingLabels(fqdn, 1), &lower);
 
@@ -958,11 +989,27 @@ mDNSexport void external_stop_resolving_service(mDNSInterfaceID InterfaceID, con
         {
             if (i == excludedTransport) continue;
             if (D2DStopResolvingPairOnTransport) D2DStopResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, i);
         {
             if (i == excludedTransport) continue;
             if (D2DStopResolvingPairOnTransport) D2DStopResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, i);
+
+            if (i == D2DAWDLTransport)
+                AWDL_used = true;
         }
     }
     else
     {
         if (D2DStopResolvingPairOnTransport) D2DStopResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, transportType);
         }
     }
     else
     {
         if (D2DStopResolvingPairOnTransport) D2DStopResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, transportType);
+
+        if (transportType == D2DAWDLTransport)
+            AWDL_used = true;
+    }
+
+    // AWDL wants the SRV and TXT record queries communicated over the D2D interface.
+    // We only want these records going to AWDL, so use AWDLInterfaceID as the
+    // interface and don't set any other flags.
+    if (AWDL_used && AWDLInterfaceID)
+    {
+        LogInfo("external_stop_resolving_service: stop browse for TXT and SRV on AWDL");
+        external_stop_browsing_for_service(AWDLInterfaceID, fqdn, kDNSType_TXT, NULL);
+        external_stop_browsing_for_service(AWDLInterfaceID, fqdn, kDNSType_SRV, NULL);
     }
 }
 
     }
 }
 
@@ -1024,6 +1071,31 @@ mDNSexport void NotifyOfElusiveBug(const char *title, const char *msg)  // Both
 #endif /* NO_CFUSERNOTIFICATION */
 }
 
 #endif /* NO_CFUSERNOTIFICATION */
 }
 
+// Returns true if it is an AppleTV based hardware running iOS, false otherwise
+mDNSlocal mDNSBool IsAppleTV(void)
+{
+#if TARGET_OS_EMBEDDED
+    static mDNSBool sInitialized = mDNSfalse;
+    static mDNSBool sIsAppleTV   = mDNSfalse;
+    CFStringRef deviceClass = NULL;
+
+    if(!sInitialized)
+    {
+        deviceClass = (CFStringRef) MGCopyAnswer(kMGQDeviceClass, NULL);
+        if(deviceClass)
+        {
+            if(CFEqual(deviceClass, kMGDeviceClassAppleTV))
+                sIsAppleTV = mDNStrue;
+            CFRelease(deviceClass);
+        }
+        sInitialized = mDNStrue;
+    }
+    return(sIsAppleTV);
+#else 
+    return mDNSfalse;
+#endif // TARGET_OS_EMBEDDED
+}
+
 mDNSlocal struct ifaddrs *myGetIfAddrs(int refresh)
 {
     static struct ifaddrs *ifa = NULL;
 mDNSlocal struct ifaddrs *myGetIfAddrs(int refresh)
 {
     static struct ifaddrs *ifa = NULL;
@@ -1034,10 +1106,155 @@ mDNSlocal struct ifaddrs *myGetIfAddrs(int refresh)
         ifa = NULL;
     }
 
         ifa = NULL;
     }
 
-    if (ifa == NULL) getifaddrs(&ifa);
+    if (ifa == NULL) 
+        getifaddrs(&ifa);
     return ifa;
 }
 
     return ifa;
 }
 
+mDNSlocal void DynamicStoreWrite(int key, const char* subkey, uintptr_t value, signed long valueCnt)
+{
+    CFStringRef sckey       = NULL;
+    Boolean release_sckey   = FALSE;
+    CFDataRef bytes         = NULL;
+    CFPropertyListRef plist = NULL;
+    SCDynamicStoreRef store = NULL;
+
+    switch ((enum mDNSDynamicStoreSetConfigKey)key)
+    {
+        case kmDNSMulticastConfig:
+            sckey = CFSTR("State:/Network/" kDNSServiceCompMulticastDNS);
+            break;
+        case kmDNSDynamicConfig:
+            sckey = CFSTR("State:/Network/DynamicDNS");
+            break;
+        case kmDNSPrivateConfig:
+            sckey = CFSTR("State:/Network/" kDNSServiceCompPrivateDNS);
+            break;
+        case kmDNSBackToMyMacConfig:
+            sckey = CFSTR("State:/Network/BackToMyMac");
+            break;
+        case kmDNSSleepProxyServersState:
+        {
+            CFMutableStringRef tmp = CFStringCreateMutable(kCFAllocatorDefault, 0);
+            CFStringAppend(tmp, CFSTR("State:/Network/Interface/"));
+            CFStringAppendCString(tmp, subkey, kCFStringEncodingUTF8);
+            CFStringAppend(tmp, CFSTR("/SleepProxyServers"));
+            sckey = CFStringCreateCopy(kCFAllocatorDefault, tmp);
+            release_sckey = TRUE;
+            CFRelease(tmp);
+            break;
+        }
+        case kmDNSDebugState:
+            sckey = CFSTR("State:/Network/mDNSResponder/DebugState");
+            break;
+        default:
+            LogMsg("unrecognized key %d", key);
+            goto fin;
+    }
+    if (NULL == (bytes = CFDataCreateWithBytesNoCopy(NULL, (void *)value,
+                                                     valueCnt, kCFAllocatorNull)))
+    {
+        LogMsg("CFDataCreateWithBytesNoCopy of value failed");
+        goto fin;
+    }
+    if (NULL == (plist = CFPropertyListCreateFromXMLData(NULL, bytes,
+                                                         kCFPropertyListImmutable, NULL)))
+    {
+        LogMsg("CFPropertyListCreateFromXMLData of bytes failed");
+        goto fin;
+    }
+    CFRelease(bytes);
+    bytes = NULL;
+    if (NULL == (store = SCDynamicStoreCreate(NULL,
+                                              CFSTR(kmDNSResponderServName), NULL, NULL)))
+    {
+        LogMsg("SCDynamicStoreCreate failed: %s", SCErrorString(SCError()));
+        goto fin;
+    }
+    SCDynamicStoreSetValue(store, sckey, plist);
+
+fin:
+    if (NULL != bytes)
+        CFRelease(bytes);
+    if (NULL != plist)
+        CFRelease(plist);
+    if (NULL != store)
+        CFRelease(store);
+    if (release_sckey && sckey)
+        CFRelease(sckey);
+}
+
+mDNSexport void mDNSDynamicStoreSetConfig(int key, const char *subkey, CFPropertyListRef value)
+{
+    CFPropertyListRef valueCopy;
+    char *subkeyCopy  = NULL;
+    if (!value)
+        return;
+
+    // We need to copy the key and value before we dispatch off the block below as the
+    // caller will free the memory once we return from this function.
+    valueCopy = CFPropertyListCreateDeepCopy(NULL, value, kCFPropertyListImmutable);
+    if (!valueCopy)
+    {   
+        LogMsg("mDNSDynamicStoreSetConfig: ERROR valueCopy NULL");
+        return;
+    }
+    if (subkey)
+    {
+        int len    = strlen(subkey);
+        subkeyCopy = mDNSPlatformMemAllocate(len + 1);
+        if (!subkeyCopy)
+        {
+            LogMsg("mDNSDynamicStoreSetConfig: ERROR subkeyCopy NULL");
+            return;
+        }
+        mDNSPlatformMemCopy(subkeyCopy, subkey, len);
+        subkeyCopy[len] = 0;
+    }
+
+    dispatch_async(DynamicStoreQueue, ^{
+        CFWriteStreamRef stream = NULL;
+        CFDataRef bytes = NULL;
+        CFStringRef error;
+        CFIndex ret;
+
+        if (NULL == (stream = CFWriteStreamCreateWithAllocatedBuffers(NULL, NULL)))
+        {
+            LogMsg("mDNSDynamicStoreSetConfig : CFWriteStreamCreateWithAllocatedBuffers failed (Object creation failed)");
+            goto END;
+        }
+        CFWriteStreamOpen(stream);
+        ret = CFPropertyListWriteToStream(valueCopy, stream, kCFPropertyListBinaryFormat_v1_0, &error);
+        if (ret == 0)
+        {
+            LogMsg("mDNSDynamicStoreSetConfig : CFPropertyListWriteToStream failed (Could not write property list to stream)");
+            goto END;
+        }
+        if (NULL == (bytes = CFWriteStreamCopyProperty(stream, kCFStreamPropertyDataWritten)))
+        {
+            LogMsg("mDNSDynamicStoreSetConfig : CFWriteStreamCopyProperty failed (Object creation failed) ");
+            goto END;
+        }
+        CFWriteStreamClose(stream);
+        CFRelease(stream);
+        stream = NULL;
+        LogInfo("mDNSDynamicStoreSetConfig: key %d subkey %s", key, subkeyCopy);
+        DynamicStoreWrite(key, subkeyCopy ? subkeyCopy : "", (uintptr_t)CFDataGetBytePtr(bytes), CFDataGetLength(bytes));
+
+    END:
+        CFRelease(valueCopy);
+        if (NULL != stream)
+        {
+            CFWriteStreamClose(stream);
+            CFRelease(stream);
+        }
+        if (NULL != bytes)
+            CFRelease(bytes); 
+        if (subkeyCopy)
+            mDNSPlatformMemFree(subkeyCopy);
+    });
+}
+
 // To match *either* a v4 or v6 instance of this interface name, pass AF_UNSPEC for type
 mDNSlocal NetworkInterfaceInfoOSX *SearchForInterfaceByName(mDNS *const m, const char *ifname, int type)
 {
 // To match *either* a v4 or v6 instance of this interface name, pass AF_UNSPEC for type
 mDNSlocal NetworkInterfaceInfoOSX *SearchForInterfaceByName(mDNS *const m, const char *ifname, int type)
 {
@@ -1050,6 +1267,52 @@ mDNSlocal NetworkInterfaceInfoOSX *SearchForInterfaceByName(mDNS *const m, const
     return(NULL);
 }
 
     return(NULL);
 }
 
+#if TARGET_OS_EMBEDDED
+mDNSlocal SCPreferencesRef mDNSManagedPrefsGet(void)
+{
+    SCPreferencesRef smDNSManagedPrefs = NULL;
+    smDNSManagedPrefs = SCPreferencesCreate(kCFAllocatorDefault, CFSTR("mDNSManagedPrefs"), kmDNSResponderManagedPrefsID);
+
+    return (smDNSManagedPrefs);
+}
+
+mDNSlocal mDNSBool GetmDNSManagedPrefKeyVal(SCPreferencesRef prefs, CFStringRef key)
+{
+    mDNSBool val = mDNSfalse;
+    CFBooleanRef val_cf = NULL;
+    if (prefs != NULL)
+    {
+        val_cf = SCPreferencesGetValue(prefs, key);
+        if (isA_CFBoolean(val_cf) != NULL)
+            val = CFBooleanGetValue(val_cf); //When mDNSResponder-Debug-profile is Installed
+        else
+            val = mDNSfalse; //When mDNSResponder-Debug-profile is Uninstalled
+    }
+    else 
+    {
+        LogMsg("GetmDNSManagedPrefKeyVal: mDNSManagedPrefs are NULL!");
+        val = mDNSfalse;
+    }
+    if (val_cf)
+        CFRelease(val_cf);
+    return (val);
+}
+
+mDNSexport mDNSBool GetmDNSManagedPref(CFStringRef key)
+{
+    SCPreferencesRef  managed = NULL;
+    mDNSBool ret_value;
+     
+    managed = mDNSManagedPrefsGet();
+    ret_value = GetmDNSManagedPrefKeyVal(managed, key);
+    
+    if (managed)
+        CFRelease(managed);
+    return (ret_value);
+}
+#endif //TARGET_OS_EMBEDDED
+
 mDNSlocal int myIfIndexToName(u_short ifindex, char *name)
 {
     struct ifaddrs *ifa;
 mDNSlocal int myIfIndexToName(u_short ifindex, char *name)
 {
     struct ifaddrs *ifa;
@@ -1121,7 +1384,8 @@ 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 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; // Only do ASL on Mac OS X 10.6 and later (not on iOS)
+    if (iOSVers) 
+        return; // No ASL on iOS
 
     static char buffer[512];
     aslmsg asl_msg = asl_new(ASL_TYPE_MSG);
 
     static char buffer[512];
     aslmsg asl_msg = asl_new(ASL_TYPE_MSG);
@@ -1151,6 +1415,230 @@ mDNSexport void mDNSASLLog(uuid_t *uuid, const char *subdomain, const char *resu
     asl_set_filter(NULL, old_filter);
     asl_free(asl_msg);
 }
     asl_set_filter(NULL, old_filter);
     asl_free(asl_msg);
 }
+
+
+mDNSlocal void mDNSLogDNSSECStatistics(mDNS *const m)
+{
+    char    buffer[16];
+
+    aslmsg  aslmsg = asl_new(ASL_TYPE_MSG);
+
+    // If we failed to allocate an aslmsg structure, keep accumulating
+    // the statistics and try again at the next log interval.
+    if (!aslmsg)
+    {
+        LogMsg("mDNSLogDNSSECStatistics: asl_new() failed!");
+        return;
+    }
+
+    asl_set(aslmsg,"com.apple.message.domain", "com.apple.mDNSResponder.DNSSECstatistics");
+
+    if (m->rrcache_totalused_unicast)
+    {
+        mDNS_snprintf(buffer, sizeof(buffer), "%u", (mDNSu32) ((unsigned long)(m->DNSSECStats.TotalMemUsed * 100))/m->rrcache_totalused_unicast);
+    }
+    else
+    {
+        LogMsg("mDNSLogDNSSECStatistics: unicast is zero");
+        buffer[0] = 0;
+    }
+    asl_set(aslmsg,"com.apple.message.MemUsage", buffer);
+
+    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.Latency0);
+    asl_set(aslmsg,"com.apple.message.Latency0", buffer);
+    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.Latency10);
+    asl_set(aslmsg,"com.apple.message.Latency10", buffer);
+    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.Latency20);
+    asl_set(aslmsg,"com.apple.message.Latency20", buffer);
+    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.Latency50);
+    asl_set(aslmsg,"com.apple.message.Latency50", buffer);
+    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.Latency100);
+    asl_set(aslmsg,"com.apple.message.Latency100", buffer);
+
+    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.ExtraPackets0);
+    asl_set(aslmsg,"com.apple.message.ExtraPackets0", buffer);
+    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.ExtraPackets3);
+    asl_set(aslmsg,"com.apple.message.ExtraPackets3", buffer);
+    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.ExtraPackets7);
+    asl_set(aslmsg,"com.apple.message.ExtraPackets7", buffer);
+    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.ExtraPackets10);
+    asl_set(aslmsg,"com.apple.message.ExtraPackets10", buffer);
+
+    // Ignore IndeterminateStatus as we don't log them
+    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.SecureStatus);
+    asl_set(aslmsg,"com.apple.message.SecureStatus", buffer);
+    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.InsecureStatus);
+    asl_set(aslmsg,"com.apple.message.InsecureStatus", buffer);
+    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.BogusStatus);
+    asl_set(aslmsg,"com.apple.message.BogusStatus", buffer);
+    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.NoResponseStatus);
+    asl_set(aslmsg,"com.apple.message.NoResponseStatus", buffer);
+
+    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.NumProbesSent);
+    asl_set(aslmsg,"com.apple.message.NumProbesSent", buffer);
+    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.MsgSize0);
+    asl_set(aslmsg,"com.apple.message.MsgSize0", buffer);
+    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.MsgSize1);
+    asl_set(aslmsg,"com.apple.message.MsgSize1", buffer);
+    mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.MsgSize2);
+    asl_set(aslmsg,"com.apple.message.MsgSize2", buffer);
+
+    asl_log(NULL, aslmsg, ASL_LEVEL_NOTICE, "");
+    asl_free(aslmsg);
+}
+
+// Calculate packets per hour given total packet count and interval in seconds.
+// Cast one term of multiplication to (long) to use 64-bit arithmetic 
+// and avoid a potential 32-bit overflow prior to the division.
+#define ONE_HOUR    3600
+#define PACKET_RATE(PACKETS, INTERVAL) (int)(((long) (PACKETS) * ONE_HOUR)/(INTERVAL))
+
+// Put packet rate data in discrete buckets.
+mDNSlocal int mDNSBucketData(int inputData, int interval)
+{
+    if (!interval)
+    {
+        LogMsg("mDNSBucketData: interval is zero!");
+        return 0;
+    }
+
+    int ratePerHour = PACKET_RATE(inputData, interval);
+    int bucket;
+
+    if (ratePerHour == 0)
+        bucket = 0;
+    else if (ratePerHour <= 10)
+        bucket = 10;
+    else if (ratePerHour <= 100)
+        bucket = 100;
+    else if (ratePerHour <= 1000)
+        bucket = 1000;
+    else if (ratePerHour <= 5000)
+        bucket = 5000;
+    else if (ratePerHour <= 10000)
+        bucket = 10000;
+    else if (ratePerHour <= 50000)
+        bucket = 50000;
+    else if (ratePerHour <= 100000)
+        bucket = 100000;
+    else if (ratePerHour <= 250000)
+        bucket = 250000;
+    else if (ratePerHour <= 500000)
+        bucket = 500000;
+    else
+        bucket = 1000000;
+
+    return bucket;
+}
+
+mDNSlocal void mDNSLogBonjourStatistics(mDNS *const m)
+{
+    static mDNSs32 last_PktNum, last_MPktNum;
+    static mDNSs32 last_UnicastPacketsSent, last_MulticastPacketsSent;
+    static mDNSs32 last_RemoteSubnet;
+
+    mDNSs32 interval;
+    char    buffer[16];
+    mDNSs32 inMulticast = m->MPktNum - last_MPktNum;
+    mDNSs32 inUnicast   = m->PktNum - last_PktNum - inMulticast;
+    mDNSs32 outUnicast  = m->UnicastPacketsSent - last_UnicastPacketsSent;
+    mDNSs32 outMulticast = m->MulticastPacketsSent - last_MulticastPacketsSent;
+    mDNSs32 remoteSubnet = m->RemoteSubnet - last_RemoteSubnet;
+
+    
+    // save starting values for new interval
+    last_PktNum = m->PktNum;
+    last_MPktNum = m->MPktNum;
+    last_UnicastPacketsSent = m->UnicastPacketsSent;
+    last_MulticastPacketsSent = m->MulticastPacketsSent;
+    last_RemoteSubnet = m->RemoteSubnet;
+
+    // Need a non-zero active time interval.
+    if (!m->ActiveStatTime)
+        return;
+
+    // Round interval time to nearest hour boundary. Less then 30 minutes rounds to zero.
+    interval = (m->ActiveStatTime + ONE_HOUR/2)/ONE_HOUR; 
+
+    // Use a minimum of 30 minutes of awake time to calculate average packet rates.
+    // The rounded awake interval should not be greater than the rounded reporting
+    // interval.
+    if ((interval == 0) || (interval > (kDefaultNextStatsticsLogTime + ONE_HOUR/2)/ONE_HOUR))
+        return;
+
+    aslmsg  aslmsg = asl_new(ASL_TYPE_MSG);
+
+    if (!aslmsg)
+    {
+        LogMsg("mDNSLogBonjourStatistics: asl_new() failed!");
+        return;
+    }
+    // log in MessageTracer format
+    asl_set(aslmsg,"com.apple.message.domain", "com.apple.mDNSResponder.statistics");
+
+    snprintf(buffer, sizeof(buffer), "%d", interval);
+    asl_set(aslmsg,"com.apple.message.interval", buffer);
+
+    // log the packet rates as packets per hour
+    snprintf(buffer, sizeof(buffer), "%d",
+            mDNSBucketData(inUnicast, m->ActiveStatTime)); 
+    asl_set(aslmsg,"com.apple.message.UnicastIn", buffer);
+
+    snprintf(buffer, sizeof(buffer), "%d",
+            mDNSBucketData(inMulticast, m->ActiveStatTime));
+    asl_set(aslmsg,"com.apple.message.MulticastIn", buffer);
+
+    snprintf(buffer, sizeof(buffer), "%d",
+            mDNSBucketData(outUnicast, m->ActiveStatTime));
+    asl_set(aslmsg,"com.apple.message.UnicastOut", buffer);
+
+    snprintf(buffer, sizeof(buffer), "%d",
+            mDNSBucketData(outMulticast, m->ActiveStatTime));
+    asl_set(aslmsg,"com.apple.message.MulticastOut", buffer);
+
+    snprintf(buffer, sizeof(buffer), "%d",
+            mDNSBucketData(remoteSubnet, m->ActiveStatTime));
+    asl_set(aslmsg,"com.apple.message.RemoteSubnet", buffer);
+
+    asl_log(NULL, aslmsg, ASL_LEVEL_NOTICE, "");
+
+    asl_free(aslmsg);
+}
+
+// Log multicast and unicast traffic statistics to MessageTracer on OSX
+mDNSexport void mDNSLogStatistics(mDNS *const m)
+{
+    // MessageTracer only available on OSX
+    if (iOSVers)
+        return;
+
+    mDNSs32 currentUTC = mDNSPlatformUTC();
+
+    // log runtime statistics
+    if ((currentUTC - m->NextStatLogTime) >= 0)
+    {
+        m->NextStatLogTime = currentUTC + kDefaultNextStatsticsLogTime;
+        // If StatStartTime is zero, it hasn't been reinitialized yet
+        // in the wakeup code path.
+        if (m->StatStartTime)
+        {
+            m->ActiveStatTime += currentUTC - m->StatStartTime;
+        }
+
+        // Only log statistics if we have recorded some active time during
+        // this statistics interval.
+        if (m->ActiveStatTime)
+        {
+            mDNSLogBonjourStatistics(m);
+            mDNSLogDNSSECStatistics(m);
+        }
+        // Start a new statistics gathering interval.
+        m->StatStartTime = currentUTC;
+        m->ActiveStatTime = 0;
+    }
+}
+
 #endif // APPLE_OSX_mDNSResponder
 
 #if COMPILER_LIKES_PRAGMA_MARK
 #endif // APPLE_OSX_mDNSResponder
 
 #if COMPILER_LIKES_PRAGMA_MARK
@@ -1162,13 +1650,10 @@ mDNSlocal mDNSBool AddrRequiresPPPConnection(const struct sockaddr *addr)
 {
     mDNSBool result = mDNSfalse;
     SCNetworkConnectionFlags flags;
 {
     mDNSBool result = mDNSfalse;
     SCNetworkConnectionFlags flags;
-#if DNSINFO_VERSION >= 20110420
     CFDataRef remote_addr;
     CFMutableDictionaryRef options;
     CFDataRef remote_addr;
     CFMutableDictionaryRef options;
-#endif // DNSINFO_VERSION >= 20110420
     SCNetworkReachabilityRef ReachRef = NULL;
 
     SCNetworkReachabilityRef ReachRef = NULL;
 
-#if DNSINFO_VERSION >= 20110420
     options = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
     remote_addr = CFDataCreate(NULL, (const UInt8 *)addr, addr->sa_len);
     CFDictionarySetValue(options, kSCNetworkReachabilityOptionRemoteAddress, remote_addr);
     options = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
     remote_addr = CFDataCreate(NULL, (const UInt8 *)addr, addr->sa_len);
     CFDictionarySetValue(options, kSCNetworkReachabilityOptionRemoteAddress, remote_addr);
@@ -1176,16 +1661,22 @@ mDNSlocal mDNSBool AddrRequiresPPPConnection(const struct sockaddr *addr)
     ReachRef = SCNetworkReachabilityCreateWithOptions(kCFAllocatorDefault, options);
     CFRelease(options);
     CFRelease(remote_addr);
     ReachRef = SCNetworkReachabilityCreateWithOptions(kCFAllocatorDefault, options);
     CFRelease(options);
     CFRelease(remote_addr);
-    if (!ReachRef) { LogMsg("ERROR: RequiresConnection - SCNetworkReachabilityCreateWithOptions"); goto end; }
-#else
-    ReachRef = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, addr);
-    if (!ReachRef) { LogMsg("ERROR: RequiresConnection - SCNetworkReachabilityCreateWithAddress"); goto end; }
-#endif //  DNSINFO_VERSION >= 20110420
-    if (!SCNetworkReachabilityGetFlags(ReachRef, &flags)) { LogMsg("ERROR: AddrRequiresPPPConnection - SCNetworkReachabilityGetFlags"); goto end; }
+
+    if (!ReachRef) 
+    { 
+        LogMsg("ERROR: RequiresConnection - SCNetworkReachabilityCreateWithOptions"); 
+        goto end; 
+    }
+    if (!SCNetworkReachabilityGetFlags(ReachRef, &flags)) 
+    { 
+        LogMsg("ERROR: AddrRequiresPPPConnection - SCNetworkReachabilityGetFlags"); 
+        goto end; 
+    }
     result = flags & kSCNetworkFlagsConnectionRequired;
 
 end:
     result = flags & kSCNetworkFlagsConnectionRequired;
 
 end:
-    if (ReachRef) CFRelease(ReachRef);
+    if (ReachRef) 
+        CFRelease(ReachRef);
     return result;
 }
 
     return result;
 }
 
@@ -1202,6 +1693,38 @@ mDNSlocal void setTrafficClass(int socketfd, mDNSBool useBackgroundTrafficClass)
     (void) setsockopt(socketfd, SOL_SOCKET, SO_TRAFFIC_CLASS, (void *)&traffic_class, sizeof(traffic_class));
 }
 
     (void) setsockopt(socketfd, SOL_SOCKET, SO_TRAFFIC_CLASS, (void *)&traffic_class, sizeof(traffic_class));
 }
 
+mDNSexport void mDNSPlatformSetDelegatePID(UDPSocket *src, const mDNSAddr *dst, DNSQuestion *q)
+{
+    if (src)
+    {
+        int s;
+
+        if (dst->type == mDNSAddrType_IPv4)
+        {
+            s = src->ss.sktv4;
+        }
+        else
+        {
+            s = src->ss.sktv6;
+        }
+
+        if (q->pid)
+        {
+            if (setsockopt(s, SOL_SOCKET, SO_DELEGATED, &q->pid, sizeof(q->pid)) == -1)
+            {
+                LogInfo("mDNSPlatformSetDelegatePID: Delegate PID failed %s for PID %d", strerror(errno), q->pid);
+            }
+        }
+        else
+        {
+            if (setsockopt(s, SOL_SOCKET, SO_DELEGATED_UUID, &q->uuid, sizeof(q->uuid)) == -1)
+            {
+                LogInfo("mDNSPlatformSetDelegatePID: Delegate UUID failed %s", strerror(errno));
+            }
+        }
+    }
+}
+
 // Note: If InterfaceID is NULL, it means, "send this packet through our anonymous unicast socket"
 // Note: If InterfaceID is non-NULL it means, "send this packet through our port 5353 socket on the specified interface"
 // OR send via our primary v4 unicast socket
 // Note: If InterfaceID is NULL, it means, "send this packet through our anonymous unicast socket"
 // Note: If InterfaceID is non-NULL it means, "send this packet through our port 5353 socket on the specified interface"
 // OR send via our primary v4 unicast socket
@@ -1284,7 +1807,7 @@ mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const ms
                 #endif
         }
     }
                 #endif
         }
     }
-#ifndef NO_IPV6
+
     else if (dst->type == mDNSAddrType_IPv6)
     {
         struct sockaddr_in6 *sin6_to = (struct sockaddr_in6*)&to;
     else if (dst->type == mDNSAddrType_IPv6)
     {
         struct sockaddr_in6 *sin6_to = (struct sockaddr_in6*)&to;
@@ -1308,7 +1831,7 @@ mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const ms
             }
         }
     }
             }
         }
     }
-#endif
+
     else
     {
         LogMsg("mDNSPlatformSendUDP: dst is not an IPv4 or IPv6 address!");
     else
     {
         LogMsg("mDNSPlatformSendUDP: dst is not an IPv4 or IPv6 address!");
@@ -1356,8 +1879,8 @@ mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const ms
                     s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, errno, strerror(errno), (mDNSu32)(m->timenow));
         else
         {
                     s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, errno, strerror(errno), (mDNSu32)(m->timenow));
         else
         {
-            MessageCount += 100;
-            if (MessageCount < 1000)  // Cap and ensure NO spamming of LogMsgs
+            MessageCount++;
+            if (MessageCount < 50)  // Cap and ensure NO spamming of LogMsgs
                 LogMsg("mDNSPlatformSendUDP: sendto(%d) failed to send packet on InterfaceID %p %5s/%d to %#a:%d skt %d error %d errno %d (%s) %lu MessageCount is %d",
                        s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, errno, strerror(errno), (mDNSu32)(m->timenow), MessageCount);
             else  // If logging is enabled, remove the cap and log aggressively
                 LogMsg("mDNSPlatformSendUDP: sendto(%d) failed to send packet on InterfaceID %p %5s/%d to %#a:%d skt %d error %d errno %d (%s) %lu MessageCount is %d",
                        s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, errno, strerror(errno), (mDNSu32)(m->timenow), MessageCount);
             else  // If logging is enabled, remove the cap and log aggressively
@@ -1379,7 +1902,7 @@ mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const ms
     return(result);
 }
 
     return(result);
 }
 
-mDNSlocal ssize_t myrecvfrom(const int s, void *const buffer, const size_t max,
+mDNSexport ssize_t myrecvfrom(const int s, void *const buffer, const size_t max,
                              struct sockaddr *const from, size_t *const fromlen, mDNSAddr *dstaddr, char ifname[IF_NAMESIZE], mDNSu8 *ttl)
 {
     static unsigned int numLogMessages = 0;
                              struct sockaddr *const from, size_t *const fromlen, mDNSAddr *dstaddr, char ifname[IF_NAMESIZE], mDNSu8 *ttl)
 {
     static unsigned int numLogMessages = 0;
@@ -1409,8 +1932,8 @@ mDNSlocal ssize_t myrecvfrom(const int s, void *const buffer, const size_t max,
     }
     if (msg.msg_controllen < (int)sizeof(struct cmsghdr))
     {
     }
     if (msg.msg_controllen < (int)sizeof(struct cmsghdr))
     {
-        if (numLogMessages++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) returned %d msg.msg_controllen %d < sizeof(struct cmsghdr) %lu",
-                                           s, n, msg.msg_controllen, sizeof(struct cmsghdr));
+        if (numLogMessages++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) returned %d msg.msg_controllen %d < sizeof(struct cmsghdr) %lu, errno %d",
+                                           s, n, msg.msg_controllen, sizeof(struct cmsghdr), errno);
         return(-1);
     }
     if (msg.msg_flags & MSG_CTRUNC)
         return(-1);
     }
     if (msg.msg_flags & MSG_CTRUNC)
@@ -1457,7 +1980,65 @@ mDNSlocal ssize_t myrecvfrom(const int s, void *const buffer, const size_t max,
     return(n);
 }
 
     return(n);
 }
 
-mDNSlocal void myKQSocketCallBack(int s1, short filter, void *context)
+mDNSlocal mDNSInterfaceID FindMyInterface(mDNS *const m, const mDNSAddr *addr)
+{
+    NetworkInterfaceInfo *intf;
+
+    if (addr->type == mDNSAddrType_IPv4)
+    {
+        for (intf = m->HostInterfaces; intf; intf = intf->next)
+        {
+            if (intf->ip.type == addr->type && intf->McastTxRx)
+            {
+                if ((intf->ip.ip.v4.NotAnInteger ^ addr->ip.v4.NotAnInteger) == 0)
+                {
+                    return(intf->InterfaceID);
+                }
+            }
+        }
+    }
+
+    if (addr->type == mDNSAddrType_IPv6)
+    {
+        for (intf = m->HostInterfaces; intf; intf = intf->next)
+        {
+            if (intf->ip.type == addr->type && intf->McastTxRx)
+            {
+                if (((intf->ip.ip.v6.l[0] ^ addr->ip.v6.l[0]) == 0) &&
+                    ((intf->ip.ip.v6.l[1] ^ addr->ip.v6.l[1]) == 0) &&
+                    ((intf->ip.ip.v6.l[2] ^ addr->ip.v6.l[2]) == 0) &&
+                    (((intf->ip.ip.v6.l[3] ^ addr->ip.v6.l[3]) == 0)))
+                    {
+                        return(intf->InterfaceID);
+                    }
+            }
+        }
+    }
+    return(mDNSInterface_Any);
+}
+
+mDNSexport mDNSBool mDNSPlatformPeekUDP(mDNS *const m, UDPSocket *src)
+{
+    // We should have a DNSMessage header followed by the question and an answer
+    // which also includes a CNAME (that's when this function is called).  To keep it
+    // simple, we expect at least the size of DNSMessage header(12) and size of "A"
+    // record (14 bytes).
+    char buffer[26];
+    int ret;
+
+    (void) m;
+
+    if (!src)
+        return mDNSfalse;
+
+    ret = recv(src->ss.sktv4, buffer, sizeof(buffer), MSG_PEEK);
+    if (ret > 0)
+        return mDNStrue;
+    else
+        return mDNSfalse;
+}
+
+mDNSexport void myKQSocketCallBack(int s1, short filter, void *context)
 {
     KQSocketSet *const ss = (KQSocketSet *)context;
     mDNS *const m = ss->m;
 {
     KQSocketSet *const ss = (KQSocketSet *)context;
     mDNS *const m = ss->m;
@@ -1466,17 +2047,10 @@ mDNSlocal void myKQSocketCallBack(int s1, short filter, void *context)
     if (filter != EVFILT_READ)
         LogMsg("myKQSocketCallBack: Why is filter %d not EVFILT_READ (%d)?", filter, EVFILT_READ);
 
     if (filter != EVFILT_READ)
         LogMsg("myKQSocketCallBack: Why is filter %d not EVFILT_READ (%d)?", filter, EVFILT_READ);
 
-    if (s1 != ss->sktv4
-#ifndef NO_IPV6
-        && s1 != ss->sktv6
-#endif
-        )
+    if (s1 != ss->sktv4 && s1 != ss->sktv6)
     {
         LogMsg("myKQSocketCallBack: native socket %d", s1);
     {
         LogMsg("myKQSocketCallBack: native socket %d", s1);
-        LogMsg("myKQSocketCallBack: sktv4 %d", ss->sktv4);
-#ifndef NO_IPV6
-        LogMsg("myKQSocketCallBack: sktv6 %d", ss->sktv6);
-#endif
+        LogMsg("myKQSocketCallBack: sktv4 %d sktv6 %d", ss->sktv4, ss->sktv6);
     }
 
     while (!closed)
     }
 
     while (!closed)
@@ -1526,8 +2100,15 @@ mDNSlocal void myKQSocketCallBack(int s1, short filter, void *context)
         // during that time, which would confuse mDNSCoreReceive, because as far
         // as it's concerned, we should have no active interfaces any more.
         // Hence we ignore multicasts for which we can find no matching InterfaceID.
         // during that time, which would confuse mDNSCoreReceive, because as far
         // as it's concerned, we should have no active interfaces any more.
         // Hence we ignore multicasts for which we can find no matching InterfaceID.
-        if (intf) InterfaceID = intf->ifinfo.InterfaceID;
-        else if (mDNSAddrIsDNSMulticast(&destAddr)) continue;
+        if (intf)
+            InterfaceID = intf->ifinfo.InterfaceID;
+        else if (mDNSAddrIsDNSMulticast(&destAddr))
+            continue;
+
+        if (!InterfaceID)
+        {
+            InterfaceID = FindMyInterface(m, &destAddr);
+        }
 
 //             LogMsg("myKQSocketCallBack got packet from %#a to %#a on interface %#a/%s",
 //                     &senderAddr, &destAddr, &ss->info->ifinfo.ip, ss->info->ifinfo.ifname);
 
 //             LogMsg("myKQSocketCallBack got packet from %#a to %#a on interface %#a/%s",
 //                     &senderAddr, &destAddr, &ss->info->ifinfo.ip, ss->info->ifinfo.ifname);
@@ -1538,7 +2119,15 @@ mDNSlocal void myKQSocketCallBack(int s1, short filter, void *context)
         // if it closes the socketset.
         ss->closeFlag = &closed;
 
         // if it closes the socketset.
         ss->closeFlag = &closed;
 
-        mDNSCoreReceive(m, &m->imsg, (unsigned char*)&m->imsg + err, &senderAddr, senderPort, &destAddr, ss->port, InterfaceID);
+        if (ss->proxy)
+        {
+            m->p->UDPProxyCallback(m, &m->p->UDPProxy, (unsigned char *)&m->imsg, (unsigned char*)&m->imsg + err, &senderAddr,
+                senderPort, &destAddr, ss->port, InterfaceID, NULL);
+        }
+        else
+        {
+            mDNSCoreReceive(m, &m->imsg, (unsigned char*)&m->imsg + err, &senderAddr, senderPort, &destAddr, ss->port, InterfaceID);
+        }
 
         // if we didn't close, we can safely dereference the socketset, and should to
         // reset the closeFlag, since it points to something on the stack
 
         // if we didn't close, we can safely dereference the socketset, and should to
         // reset the closeFlag, since it points to something on the stack
@@ -1586,37 +2175,7 @@ mDNSlocal void myKQSocketCallBack(int s1, short filter, void *context)
     }
 }
 
     }
 }
 
-// TCP socket support
-
-typedef enum
-{
-    handshake_required,
-    handshake_in_progress,
-    handshake_completed,
-    handshake_to_be_closed
-} handshakeStatus;
-
-struct TCPSocket_struct
-{
-    TCPSocketFlags flags;       // MUST BE FIRST FIELD -- mDNSCore expects every TCPSocket_struct to begin with TCPSocketFlags flags
-    TCPConnectionCallback callback;
-    int fd;
-    KQueueEntry *kqEntry;
-    KQSocketSet ss;
-#ifndef NO_SECURITYFRAMEWORK
-    SSLContextRef tlsContext;
-    pthread_t handshake_thread;
-#endif /* NO_SECURITYFRAMEWORK */
-    domainname hostname;
-    void *context;
-    mDNSBool setup;
-    mDNSBool connected;
-    handshakeStatus handshake;
-    mDNS *m; // So we can call KQueueLock from the SSLHandshake thread
-    mStatus err;
-};
-
-mDNSlocal void doTcpSocketCallback(TCPSocket *sock)
+mDNSlocal void doTcpSocketCallback(TCPSocket *sock)
 {
     mDNSBool c = !sock->connected;
     sock->connected = mDNStrue;
 {
     mDNSBool c = !sock->connected;
     sock->connected = mDNStrue;
@@ -1652,39 +2211,68 @@ mDNSlocal OSStatus tlsReadSock(SSLConnectionRef connection, void *data, size_t *
     return(errSSLClosedAbort);
 }
 
     return(errSSLClosedAbort);
 }
 
-mDNSlocal OSStatus tlsSetupSock(TCPSocket *sock, mDNSBool server)
+mDNSlocal OSStatus tlsSetupSock(TCPSocket *sock, SSLProtocolSide pside, SSLConnectionType ctype)
 {
     char domname_cstr[MAX_ESCAPED_DOMAIN_NAME];
 
 {
     char domname_cstr[MAX_ESCAPED_DOMAIN_NAME];
 
-    mStatus err = SSLNewContext(server, &sock->tlsContext);
-    if (err) { LogMsg("ERROR: tlsSetupSock: SSLNewContext failed with error code: %d", err); return(err); }
+    sock->tlsContext = SSLCreateContext(kCFAllocatorDefault, pside, ctype);
+    if (!sock->tlsContext) 
+    { 
+        LogMsg("ERROR: tlsSetupSock: SSLCreateContext failed"); 
+        return(mStatus_UnknownErr); 
+    }
 
 
-    err = SSLSetIOFuncs(sock->tlsContext, tlsReadSock, tlsWriteSock);
-    if (err) { LogMsg("ERROR: tlsSetupSock: SSLSetIOFuncs failed with error code: %d", err); return(err); }
+    mStatus err = SSLSetIOFuncs(sock->tlsContext, tlsReadSock, tlsWriteSock);
+    if (err) 
+    { 
+        LogMsg("ERROR: tlsSetupSock: SSLSetIOFuncs failed with error code: %d", err); 
+        goto fail; 
+    }
 
     err = SSLSetConnection(sock->tlsContext, (SSLConnectionRef) sock);
 
     err = SSLSetConnection(sock->tlsContext, (SSLConnectionRef) sock);
-    if (err) { LogMsg("ERROR: tlsSetupSock: SSLSetConnection failed with error code: %d", err); return(err); }
+    if (err) 
+    { 
+        LogMsg("ERROR: tlsSetupSock: SSLSetConnection failed with error code: %d", err); 
+        goto fail; 
+    }
 
     // Instead of listing all the acceptable ciphers, we just disable the bad ciphers. It does not disable
     // all the bad ciphers like RC4_MD5, but it assumes that the servers don't offer them.
     err = SSLSetAllowAnonymousCiphers(sock->tlsContext, 0);
 
     // Instead of listing all the acceptable ciphers, we just disable the bad ciphers. It does not disable
     // all the bad ciphers like RC4_MD5, but it assumes that the servers don't offer them.
     err = SSLSetAllowAnonymousCiphers(sock->tlsContext, 0);
-    if (err) { LogMsg("ERROR: tlsSetupSock: SSLSetAllowAnonymousCiphers failed with error code: %d", err); return(err); }
+    if (err) 
+    { 
+        LogMsg("ERROR: tlsSetupSock: SSLSetAllowAnonymousCiphers failed with error code: %d", err); 
+        goto fail; 
+    }
 
     // We already checked for NULL in hostname and this should never happen. Hence, returning -1
     // (error not in OSStatus space) is okay.
 
     // We already checked for NULL in hostname and this should never happen. Hence, returning -1
     // (error not in OSStatus space) is okay.
-    if (!sock->hostname.c[0]) {LogMsg("ERROR: tlsSetupSock: hostname NULL"); return -1; }
+    if (!sock->hostname.c[0]) 
+    {
+        LogMsg("ERROR: tlsSetupSock: hostname NULL"); 
+        err = -1;
+        goto fail;
+    }
 
     ConvertDomainNameToCString(&sock->hostname, domname_cstr);
     err = SSLSetPeerDomainName(sock->tlsContext, domname_cstr, strlen(domname_cstr));
 
     ConvertDomainNameToCString(&sock->hostname, domname_cstr);
     err = SSLSetPeerDomainName(sock->tlsContext, domname_cstr, strlen(domname_cstr));
-    if (err) { LogMsg("ERROR: tlsSetupSock: SSLSetPeerDomainname: %s failed with error code: %d", domname_cstr, err); return(err); }
+    if (err) 
+    { 
+        LogMsg("ERROR: tlsSetupSock: SSLSetPeerDomainname: %s failed with error code: %d", domname_cstr, err); 
+        goto fail; 
+    }
+
+    return(err);
 
 
+fail:
+    if (sock->tlsContext)
+        CFRelease(sock->tlsContext);
     return(err);
 }
 
 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
     return(err);
 }
 
 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
-mDNSlocal void doSSLHandshake(void *ctx)
+mDNSlocal void doSSLHandshake(TCPSocket *sock)
 {
 {
-    TCPSocket *sock = (TCPSocket*)ctx;
     mStatus err = SSLHandshake(sock->tlsContext);
 
     //Can't have multiple threads in mDNS core. When MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM is
     mStatus err = SSLHandshake(sock->tlsContext);
 
     //Can't have multiple threads in mDNS core. When MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM is
@@ -1720,7 +2308,7 @@ mDNSlocal void doSSLHandshake(void *ctx)
                                if (err)
                                {
                                    LogMsg("SSLHandshake failed: %d%s", err, err == errSSLPeerInternalError ? " (server busy)" : "");
                                if (err)
                                {
                                    LogMsg("SSLHandshake failed: %d%s", err, err == errSSLPeerInternalError ? " (server busy)" : "");
-                                   SSLDisposeContext(sock->tlsContext);
+                                   CFRelease(sock->tlsContext);
                                    sock->tlsContext = NULL;
                                }
 
                                    sock->tlsContext = NULL;
                                }
 
@@ -1736,12 +2324,11 @@ mDNSlocal void doSSLHandshake(void *ctx)
                        return;
                    });
 }
                        return;
                    });
 }
-#else
-mDNSlocal void *doSSLHandshake(void *ctx)
+#else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
+mDNSlocal void *doSSLHandshake(TCPSocket *sock)
 {
     // Warning: Touching sock without the kqueue lock!
     // We're protected because sock->handshake == handshake_in_progress
 {
     // Warning: Touching sock without the kqueue lock!
     // We're protected because sock->handshake == handshake_in_progress
-    TCPSocket *sock = (TCPSocket*)ctx;
     mDNS * const m = sock->m; // Get m now, as we may free sock if marked to be closed while we're waiting on SSLHandshake
     mStatus err = SSLHandshake(sock->tlsContext);
 
     mDNS * const m = sock->m; // Get m now, as we may free sock if marked to be closed while we're waiting on SSLHandshake
     mStatus err = SSLHandshake(sock->tlsContext);
 
@@ -1765,7 +2352,7 @@ mDNSlocal void *doSSLHandshake(void *ctx)
             if (err)
             {
                 LogMsg("SSLHandshake failed: %d%s", err, err == errSSLPeerInternalError ? " (server busy)" : "");
             if (err)
             {
                 LogMsg("SSLHandshake failed: %d%s", err, err == errSSLPeerInternalError ? " (server busy)" : "");
-                SSLDisposeContext(sock->tlsContext);
+                CFRelease(sock->tlsContext);
                 sock->tlsContext = NULL;
             }
 
                 sock->tlsContext = NULL;
             }
 
@@ -1781,37 +2368,21 @@ mDNSlocal void *doSSLHandshake(void *ctx)
     KQueueUnlock(m, "doSSLHandshake");
     return NULL;
 }
     KQueueUnlock(m, "doSSLHandshake");
     return NULL;
 }
-#endif
+#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 
 
-mDNSlocal mStatus spawnSSLHandshake(TCPSocket* sock)
+mDNSlocal void spawnSSLHandshake(TCPSocket* sock)
 {
     debugf("spawnSSLHandshake %p: entry", sock);
 {
     debugf("spawnSSLHandshake %p: entry", sock);
-    mStatus err;
 
     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);
 
     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 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 it on a separate queue to help avoid blocking other threads/queues, and
+    // to limit the number of threads used for SSLHandshake
     dispatch_async(SSLqueue, ^{doSSLHandshake(sock);});
     dispatch_async(SSLqueue, ^{doSSLHandshake(sock);});
-    err = 0;
-#else
-    pthread_attr_t attr;
-    pthread_attr_init(&attr);
-    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-    err = pthread_create(&sock->handshake_thread, &attr, doSSLHandshake, sock);
-    pthread_attr_destroy(&attr);
-    if (err)
-    {
-        LogMsg("Could not start SSLHandshake thread: (%d) %s", err, strerror(err));
-        sock->handshake = handshake_completed;
-        sock->err = err;
-        KQueueSet(sock->fd, EV_ADD, EVFILT_READ, sock->kqEntry);
-    }
-#endif
+
     debugf("spawnSSLHandshake %p: done for %d", sock, sock->fd);
     debugf("spawnSSLHandshake %p: done for %d", sock, sock->fd);
-    return err;
 }
 
 #endif /* NO_SECURITYFRAMEWORK */
 }
 
 #endif /* NO_SECURITYFRAMEWORK */
@@ -1824,21 +2395,38 @@ mDNSlocal void tcpKQSocketCallback(__unused int fd, short filter, void *context)
     //if (filter == EVFILT_READ ) LogMsg("myKQSocketCallBack: tcpKQSocketCallback %d is EVFILT_READ", filter);
     //if (filter == EVFILT_WRITE) LogMsg("myKQSocketCallBack: tcpKQSocketCallback %d is EVFILT_WRITE", filter);
     // EV_ONESHOT doesn't seem to work, so we add the filter with EV_ADD, and explicitly delete it here with EV_DELETE
     //if (filter == EVFILT_READ ) LogMsg("myKQSocketCallBack: tcpKQSocketCallback %d is EVFILT_READ", filter);
     //if (filter == EVFILT_WRITE) LogMsg("myKQSocketCallBack: tcpKQSocketCallback %d is EVFILT_WRITE", filter);
     // EV_ONESHOT doesn't seem to work, so we add the filter with EV_ADD, and explicitly delete it here with EV_DELETE
-    if (filter == EVFILT_WRITE) KQueueSet(sock->fd, EV_DELETE, EVFILT_WRITE, sock->kqEntry);
+    if (filter == EVFILT_WRITE) 
+        KQueueSet(sock->fd, EV_DELETE, EVFILT_WRITE, sock->kqEntry);
 
     if (sock->flags & kTCPSocketFlags_UseTLS)
     {
 #ifndef NO_SECURITYFRAMEWORK
 
     if (sock->flags & kTCPSocketFlags_UseTLS)
     {
 #ifndef NO_SECURITYFRAMEWORK
-        if (!sock->setup) { sock->setup = mDNStrue; tlsSetupSock(sock, mDNSfalse); }
-
-        if (sock->handshake == handshake_required) { if (spawnSSLHandshake(sock) == 0) return;}
-        else if (sock->handshake == handshake_in_progress || sock->handshake == handshake_to_be_closed) return;
+        if (!sock->setup) 
+        { 
+            sock->setup = mDNStrue; 
+            sock->err = tlsSetupSock(sock, kSSLClientSide, kSSLStreamType);
+            if (sock->err)
+            {
+                LogMsg("ERROR: tcpKQSocketCallback: tlsSetupSock failed with error code: %d", sock->err);
+                return;
+            }
+        }
+        if (sock->handshake == handshake_required) 
+        { 
+            spawnSSLHandshake(sock); 
+            return;
+        }
+        else if (sock->handshake == handshake_in_progress || sock->handshake == handshake_to_be_closed)
+        {
+            return;
+        }
         else if (sock->handshake != handshake_completed)
         {
         else if (sock->handshake != handshake_completed)
         {
-            if (!sock->err) sock->err = mStatus_UnknownErr;
+            if (!sock->err) 
+                sock->err = mStatus_UnknownErr;
             LogMsg("tcpKQSocketCallback called with unexpected SSLHandshake status: %d", sock->handshake);
         }
             LogMsg("tcpKQSocketCallback called with unexpected SSLHandshake status: %d", sock->handshake);
         }
-#else
+#else  /* NO_SECURITYFRAMEWORK */ 
         sock->err = mStatus_UnsupportedErr;
 #endif /* NO_SECURITYFRAMEWORK */
     }
         sock->err = mStatus_UnsupportedErr;
 #endif /* NO_SECURITYFRAMEWORK */
     }
@@ -1940,7 +2528,7 @@ mDNSexport void KQueueLock(mDNS *const m)
     m->p->BigMutexStartTime = mDNSPlatformRawTime();
 }
 
     m->p->BigMutexStartTime = mDNSPlatformRawTime();
 }
 
-mDNSexport void KQueueUnlock(mDNS *const m, const char const *task)
+mDNSexport void KQueueUnlock(mDNS *const m, const chartask)
 {
     mDNSs32 end = mDNSPlatformRawTime();
     (void)task;
 {
     mDNSs32 end = mDNSPlatformRawTime();
     (void)task;
@@ -1981,13 +2569,8 @@ mDNSexport void mDNSPlatformCloseFD(KQueueEntry *kq, int fd)
 mDNSlocal mStatus SetupTCPSocket(TCPSocket *sock, u_short sa_family, mDNSIPPort *port, mDNSBool useBackgroundTrafficClass)
 {
     KQSocketSet *cp = &sock->ss;
 mDNSlocal mStatus SetupTCPSocket(TCPSocket *sock, u_short sa_family, mDNSIPPort *port, mDNSBool useBackgroundTrafficClass)
 {
     KQSocketSet *cp = &sock->ss;
-#ifndef NO_IPV6
     int         *s        = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6;
     KQueueEntry *k        = (sa_family == AF_INET) ? &cp->kqsv4 : &cp->kqsv6;
     int         *s        = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6;
     KQueueEntry *k        = (sa_family == AF_INET) ? &cp->kqsv4 : &cp->kqsv6;
-#else
-    int         *s        = &cp->sktv4;
-    KQueueEntry *k        = &cp->kqsv4;
-#endif
     const int on = 1;  // "on" for setsockopt
     mStatus err;
 
     const int on = 1;  // "on" for setsockopt
     mStatus err;
 
@@ -2064,17 +2647,14 @@ mDNSexport TCPSocket *mDNSPlatformTCPSocket(mDNS *const m, TCPSocketFlags flags,
 
     sock->ss.m     = m;
     sock->ss.sktv4 = -1;
 
     sock->ss.m     = m;
     sock->ss.sktv4 = -1;
-#ifndef NO_IPV6
     sock->ss.sktv6 = -1;
     sock->ss.sktv6 = -1;
-#endif
     err = SetupTCPSocket(sock, AF_INET, port, useBackgroundTrafficClass);
     err = SetupTCPSocket(sock, AF_INET, port, useBackgroundTrafficClass);
-#ifndef NO_IPV6
+
     if (!err)
     {
         err = SetupTCPSocket(sock, AF_INET6, port, useBackgroundTrafficClass);
         if (err) { mDNSPlatformCloseFD(&sock->ss.kqsv4, sock->ss.sktv4); sock->ss.sktv4 = -1; }
     }
     if (!err)
     {
         err = SetupTCPSocket(sock, AF_INET6, port, useBackgroundTrafficClass);
         if (err) { mDNSPlatformCloseFD(&sock->ss.kqsv4, sock->ss.sktv4); sock->ss.sktv4 = -1; }
     }
-#endif
     if (err)
     {
         LogMsg("mDNSPlatformTCPSocket: socket error %d errno %d (%s)", sock->fd, errno, strerror(errno));
     if (err)
     {
         LogMsg("mDNSPlatformTCPSocket: socket error %d errno %d (%s)", sock->fd, errno, strerror(errno));
@@ -2098,13 +2678,8 @@ mDNSexport TCPSocket *mDNSPlatformTCPSocket(mDNS *const m, TCPSocketFlags flags,
 mDNSexport mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr *dst, mDNSOpaque16 dstport, domainname *hostname, mDNSInterfaceID InterfaceID, TCPConnectionCallback callback, void *context)
 {
     KQSocketSet *cp = &sock->ss;
 mDNSexport mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr *dst, mDNSOpaque16 dstport, domainname *hostname, mDNSInterfaceID InterfaceID, TCPConnectionCallback callback, void *context)
 {
     KQSocketSet *cp = &sock->ss;
-#ifndef NO_IPV6
     int         *s        = (dst->type == mDNSAddrType_IPv4) ? &cp->sktv4 : &cp->sktv6;
     KQueueEntry *k        = (dst->type == mDNSAddrType_IPv4) ? &cp->kqsv4 : &cp->kqsv6;
     int         *s        = (dst->type == mDNSAddrType_IPv4) ? &cp->sktv4 : &cp->sktv6;
     KQueueEntry *k        = (dst->type == mDNSAddrType_IPv4) ? &cp->kqsv4 : &cp->kqsv6;
-#else
-    int         *s        = &cp->sktv4;
-    KQueueEntry *k        = &cp->kqsv4;
-#endif
     mStatus err = mStatus_NoError;
     struct sockaddr_storage ss;
 
     mStatus err = mStatus_NoError;
     struct sockaddr_storage ss;
 
@@ -2225,7 +2800,7 @@ mDNSexport TCPSocket *mDNSPlatformTCPAccept(TCPSocketFlags flags, int fd)
 #ifndef NO_SECURITYFRAMEWORK
         if (!ServerCerts) { LogMsg("ERROR: mDNSPlatformTCPAccept: unable to find TLS certificates"); err = mStatus_UnknownErr; goto exit; }
 
 #ifndef NO_SECURITYFRAMEWORK
         if (!ServerCerts) { LogMsg("ERROR: mDNSPlatformTCPAccept: unable to find TLS certificates"); err = mStatus_UnknownErr; goto exit; }
 
-        err = tlsSetupSock(sock, mDNStrue);
+        err = tlsSetupSock(sock, kSSLServerSide, kSSLStreamType);
         if (err) { LogMsg("ERROR: mDNSPlatformTCPAccept: tlsSetupSock failed with error code: %d", err); goto exit; }
 
         err = SSLSetCertificate(sock->tlsContext, ServerCerts);
         if (err) { LogMsg("ERROR: mDNSPlatformTCPAccept: tlsSetupSock failed with error code: %d", err); goto exit; }
 
         err = SSLSetCertificate(sock->tlsContext, ServerCerts);
@@ -2242,6 +2817,18 @@ exit:
     return(sock);
 }
 
     return(sock);
 }
 
+mDNSexport mDNSu16 mDNSPlatformGetUDPPort(UDPSocket *sock)
+{
+    mDNSu16 port;
+
+    port = -1;
+    if (sock)
+    {
+        port = sock->ss.port.NotAnInteger;
+    }
+    return port;
+}
+
 mDNSlocal void CloseSocketSet(KQSocketSet *ss)
 {
     if (ss->sktv4 != -1)
 mDNSlocal void CloseSocketSet(KQSocketSet *ss)
 {
     if (ss->sktv4 != -1)
@@ -2249,13 +2836,11 @@ mDNSlocal void CloseSocketSet(KQSocketSet *ss)
         mDNSPlatformCloseFD(&ss->kqsv4,  ss->sktv4);
         ss->sktv4 = -1;
     }
         mDNSPlatformCloseFD(&ss->kqsv4,  ss->sktv4);
         ss->sktv4 = -1;
     }
-#ifndef NO_IPV6
     if (ss->sktv6 != -1)
     {
         mDNSPlatformCloseFD(&ss->kqsv6,  ss->sktv6);
         ss->sktv6 = -1;
     }
     if (ss->sktv6 != -1)
     {
         mDNSPlatformCloseFD(&ss->kqsv6,  ss->sktv6);
         ss->sktv6 = -1;
     }
-#endif
     if (ss->closeFlag) *ss->closeFlag = 1;
 }
 
     if (ss->closeFlag) *ss->closeFlag = 1;
 }
 
@@ -2276,14 +2861,14 @@ mDNSexport void mDNSPlatformTCPCloseConnection(TCPSocket *sock)
             }
 
             SSLClose(sock->tlsContext);
             }
 
             SSLClose(sock->tlsContext);
-            SSLDisposeContext(sock->tlsContext);
+            CFRelease(sock->tlsContext);
             sock->tlsContext = NULL;
         }
 #endif /* NO_SECURITYFRAMEWORK */
             sock->tlsContext = NULL;
         }
 #endif /* NO_SECURITYFRAMEWORK */
-        if (sock->ss.sktv4 != -1) shutdown(sock->ss.sktv4, 2);
-#ifndef NO_IPV6
-        if (sock->ss.sktv6 != -1) shutdown(sock->ss.sktv6, 2);
-#endif
+        if (sock->ss.sktv4 != -1) 
+            shutdown(sock->ss.sktv4, 2);
+        if (sock->ss.sktv6 != -1) 
+            shutdown(sock->ss.sktv6, 2);
         CloseSocketSet(&sock->ss);
         sock->fd = -1;
 
         CloseSocketSet(&sock->ss);
         sock->fd = -1;
 
@@ -2293,7 +2878,7 @@ mDNSexport void mDNSPlatformTCPCloseConnection(TCPSocket *sock)
 
 mDNSexport long mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long buflen, mDNSBool *closed)
 {
 
 mDNSexport long mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long buflen, mDNSBool *closed)
 {
-    size_t nread = 0;
+    ssize_t nread = 0;
     *closed = mDNSfalse;
 
     if (sock->flags & kTCPSocketFlags_UseTLS)
     *closed = mDNSfalse;
 
     if (sock->flags & kTCPSocketFlags_UseTLS)
@@ -2304,7 +2889,7 @@ mDNSexport long mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long bu
         else if (sock->handshake != handshake_completed) LogMsg("mDNSPlatformReadTCP called with unexpected SSLHandshake status: %d", sock->handshake);
 
         //LogMsg("Starting SSLRead %d %X", sock->fd, fcntl(sock->fd, F_GETFL, 0));
         else if (sock->handshake != handshake_completed) LogMsg("mDNSPlatformReadTCP called with unexpected SSLHandshake status: %d", sock->handshake);
 
         //LogMsg("Starting SSLRead %d %X", sock->fd, fcntl(sock->fd, F_GETFL, 0));
-        mStatus err = SSLRead(sock->tlsContext, buf, buflen, &nread);
+        mStatus err = SSLRead(sock->tlsContext, buf, buflen, (size_t *)&nread);
         //LogMsg("SSLRead returned %d (%d) nread %d buflen %d", err, errSSLWouldBlock, nread, buflen);
         if (err == errSSLClosedGraceful) { nread = 0; *closed = mDNStrue; }
         else if (err && err != errSSLWouldBlock)
         //LogMsg("SSLRead returned %d (%d) nread %d buflen %d", err, errSSLWouldBlock, nread, buflen);
         if (err == errSSLClosedGraceful) { nread = 0; *closed = mDNStrue; }
         else if (err && err != errSSLWouldBlock)
@@ -2395,21 +2980,13 @@ mDNSexport int mDNSPlatformTCPGetFD(TCPSocket *sock)
 // If mDNSIPPort port is zero, then it's a randomly assigned port number, used for sending unicast queries
 mDNSlocal mStatus SetupSocket(KQSocketSet *cp, const mDNSIPPort port, u_short sa_family, mDNSIPPort *const outport)
 {
 // If mDNSIPPort port is zero, then it's a randomly assigned port number, used for sending unicast queries
 mDNSlocal mStatus SetupSocket(KQSocketSet *cp, const mDNSIPPort port, u_short sa_family, mDNSIPPort *const outport)
 {
-#ifndef NO_IPV6
     int         *s        = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6;
     KQueueEntry *k        = (sa_family == AF_INET) ? &cp->kqsv4 : &cp->kqsv6;
     int         *s        = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6;
     KQueueEntry *k        = (sa_family == AF_INET) ? &cp->kqsv4 : &cp->kqsv6;
-#else
-    int         *s        = &cp->sktv4;
-    KQueueEntry *k        = &cp->kqsv4;
-#endif
     const int on = 1;
     const int twofivefive = 255;
     mStatus err = mStatus_NoError;
     char *errstr = mDNSNULL;
     const int on = 1;
     const int twofivefive = 255;
     mStatus err = mStatus_NoError;
     char *errstr = mDNSNULL;
-
-#ifdef NO_IPV6
-    if (sa_family != AF_INET) return -1;
-#endif
+    const int mtu = 0;
 
     cp->closeFlag = mDNSNULL;
 
 
     cp->closeFlag = mDNSNULL;
 
@@ -2465,10 +3042,9 @@ mDNSlocal mStatus SetupSocket(KQSocketSet *cp, const mDNSIPPort port, u_short sa
         if (err) { errstr = "bind"; goto fail; }
         if (outport) outport->NotAnInteger = listening_sockaddr.sin_port;
     }
         if (err) { errstr = "bind"; goto fail; }
         if (outport) outport->NotAnInteger = listening_sockaddr.sin_port;
     }
-#ifndef NO_IPV6
     else if (sa_family == AF_INET6)
     {
     else if (sa_family == AF_INET6)
     {
-        // NAT-PMP Announcements make no sense on IPv6, so bail early w/o error
+        // NAT-PMP Announcements make no sense on IPv6, and we don't support IPv6 for PCP, so bail early w/o error
         if (mDNSSameIPPort(port, NATPMPAnnouncementPort)) { if (outport) *outport = zeroIPPort;return mStatus_NoError; }
 
         // We want to receive destination addresses and receive interface identifiers
         if (mDNSSameIPPort(port, NATPMPAnnouncementPort)) { if (outport) *outport = zeroIPPort;return mStatus_NoError; }
 
         // We want to receive destination addresses and receive interface identifiers
@@ -2496,6 +3072,12 @@ mDNSlocal mStatus SetupSocket(KQSocketSet *cp, const mDNSIPPort port, u_short sa
         err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(on));
         if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_LOOP"; goto fail; }
 
         err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(on));
         if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_LOOP"; goto fail; }
 
+        // Disable default option to send mDNSv6 packets at min IPv6 MTU: RFC 3542, Sec 11
+        err = setsockopt(skt, IPPROTO_IPV6, IPV6_USE_MIN_MTU, &mtu, sizeof(mtu));
+        if (err < 0) // Since it is an optimization if we fail just log the err, no need to close the skt
+            LogMsg("SetupSocket: setsockopt - IPV6_USE_MIN_MTU: IP6PO_MINMTU_DISABLE socket %d err %d errno %d (%s)", 
+                    skt, err, errno, strerror(errno));
+        
         // And start listening for packets
         struct sockaddr_in6 listening_sockaddr6;
         mDNSPlatformMemZero(&listening_sockaddr6, sizeof(listening_sockaddr6));
         // And start listening for packets
         struct sockaddr_in6 listening_sockaddr6;
         mDNSPlatformMemZero(&listening_sockaddr6, sizeof(listening_sockaddr6));
@@ -2509,7 +3091,6 @@ mDNSlocal mStatus SetupSocket(KQSocketSet *cp, const mDNSIPPort port, u_short sa
         if (err) { errstr = "bind"; goto fail; }
         if (outport) outport->NotAnInteger = listening_sockaddr6.sin6_port;
     }
         if (err) { errstr = "bind"; goto fail; }
         if (outport) outport->NotAnInteger = listening_sockaddr6.sin6_port;
     }
-#endif
 
     fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK); // set non-blocking
     fcntl(skt, F_SETFD, 1); // set close-on-exec
 
     fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK); // set non-blocking
     fcntl(skt, F_SETFD, 1); // set close-on-exec
@@ -2559,22 +3140,19 @@ mDNSexport UDPSocket *mDNSPlatformUDPSocket(mDNS *const m, const mDNSIPPort requ
     p->ss.port  = zeroIPPort;
     p->ss.m     = m;
     p->ss.sktv4 = -1;
     p->ss.port  = zeroIPPort;
     p->ss.m     = m;
     p->ss.sktv4 = -1;
-#ifndef NO_IPV6
     p->ss.sktv6 = -1;
     p->ss.sktv6 = -1;
-#endif
+    p->ss.proxy = mDNSfalse;
 
     do
     {
         // The kernel doesn't do cryptographically strong random port allocation, so we do it ourselves here
         if (randomizePort) port = mDNSOpaque16fromIntVal(0xC000 + mDNSRandom(0x3FFF));
         err = SetupSocket(&p->ss, port, AF_INET, &p->ss.port);
 
     do
     {
         // The kernel doesn't do cryptographically strong random port allocation, so we do it ourselves here
         if (randomizePort) port = mDNSOpaque16fromIntVal(0xC000 + mDNSRandom(0x3FFF));
         err = SetupSocket(&p->ss, port, AF_INET, &p->ss.port);
-#ifndef NO_IPV6
         if (!err)
         {
             err = SetupSocket(&p->ss, port, AF_INET6, &p->ss.port);
             if (err) { mDNSPlatformCloseFD(&p->ss.kqsv4, p->ss.sktv4); p->ss.sktv4 = -1; }
         }
         if (!err)
         {
             err = SetupSocket(&p->ss, port, AF_INET6, &p->ss.port);
             if (err) { mDNSPlatformCloseFD(&p->ss.kqsv4, p->ss.sktv4); p->ss.sktv4 = -1; }
         }
-#endif
         i--;
     } while (err == EADDRINUSE && randomizePort && i);
 
         i--;
     } while (err == EADDRINUSE && randomizePort && i);
 
@@ -2635,7 +3213,7 @@ mDNSexport void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSA
     if (info == NULL) { LogMsg("mDNSPlatformSetLocalAddressCacheEntry: Invalid interface index %p", InterfaceID); return; }
     // Manually inject an entry into our local ARP cache.
     // (We can't do this by sending an ARP broadcast, because the kernel only pays attention to incoming ARP packets, not outgoing.)
     if (info == NULL) { LogMsg("mDNSPlatformSetLocalAddressCacheEntry: Invalid interface index %p", InterfaceID); return; }
     // Manually inject an entry into our local ARP cache.
     // (We can't do this by sending an ARP broadcast, because the kernel only pays attention to incoming ARP packets, not outgoing.)
-    if (!mDNS_AddressIsLocalSubnet(m, InterfaceID, tpa))
+    if (!mDNS_AddressIsLocalSubnet(m, InterfaceID, tpa, mDNSNULL))
         LogSPS("Don't need address cache entry for %s %#a %.6a",            info->ifinfo.ifname, tpa, tha);
     else
     {
         LogSPS("Don't need address cache entry for %s %#a %.6a",            info->ifinfo.ifname, tpa, tha);
     else
     {
@@ -2734,6 +3312,38 @@ mDNSexport void mDNSPlatformSendKeepalive(mDNSAddr *sadd, mDNSAddr *dadd, mDNSIP
     mDNSSendKeepalive(sadd->ip.v6.b, dadd->ip.v6.b, lport->NotAnInteger, rport->NotAnInteger, seq, ack, win);
 }
 
     mDNSSendKeepalive(sadd->ip.v6.b, dadd->ip.v6.b, lport->NotAnInteger, rport->NotAnInteger, seq, ack, win);
 }
 
+mDNSexport mStatus mDNSPlatformStoreSPSMACAddr(mDNSAddr *spsaddr, char *ifname)
+{
+    int family = (spsaddr->type == mDNSAddrType_IPv4) ? AF_INET : AF_INET6;
+    LogSPS("mDNSPlatformStoreSPSMACAddr : Storing %#a on interface %s", spsaddr, ifname);
+    mDNSStoreSPSMACAddress(family, spsaddr->ip.v6.b, ifname);
+    return KERN_SUCCESS;
+}
+
+mDNSexport mStatus mDNSPlatformGetRemoteMacAddr(mDNS *const m, mDNSAddr *raddr)
+{
+    int family = (raddr->type == mDNSAddrType_IPv4) ? AF_INET : AF_INET6;
+
+    mDNSGetRemoteMAC(m, family, raddr->ip.v6.b);
+    return KERN_SUCCESS;
+}
+
+mDNSexport mStatus mDNSPlatformRetrieveTCPInfo(mDNS *const m, mDNSAddr *laddr, mDNSIPPort *lport, mDNSAddr *raddr, mDNSIPPort *rport, mDNSTCPInfo *mti)
+{
+    mDNSs32 intfid;
+    mDNSs32 error  = 0;
+    int     family = (laddr->type == mDNSAddrType_IPv4) ? AF_INET : AF_INET6;
+
+    error = mDNSRetrieveTCPInfo(family, laddr->ip.v6.b, lport->NotAnInteger, raddr->ip.v6.b, rport->NotAnInteger, (uint32_t *)&(mti->seq), (uint32_t *)&(mti->ack), (uint16_t *)&(mti->window), (int32_t*)&intfid);
+    if (error != KERN_SUCCESS)
+    {
+        LogMsg("%s: mDNSRetrieveTCPInfo returned : %d", __func__, error);
+        return error;
+    }
+    mti->IntfId = mDNSPlatformInterfaceIDfromInterfaceIndex(m, intfid);
+    return error;
+}
+
 #define BPF_SetOffset(from, cond, to) (from)->cond = (to) - 1 - (from)
 
 mDNSlocal int CountProxyTargets(mDNS *const m, NetworkInterfaceInfoOSX *x, int *p4, int *p6)
 #define BPF_SetOffset(from, cond, to) (from)->cond = (to) - 1 - (from)
 
 mDNSlocal int CountProxyTargets(mDNS *const m, NetworkInterfaceInfoOSX *x, int *p4, int *p6)
@@ -2919,9 +3529,9 @@ mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID
         // prog.bf_len = 0; This seems to panic the kernel
         if (x->BPF_fd < 0) return;      // If we've already closed our BPF_fd, no need to generate an error message below
     }
         // prog.bf_len = 0; This seems to panic the kernel
         if (x->BPF_fd < 0) return;      // If we've already closed our BPF_fd, no need to generate an error message below
     }
-
-    if (ioctl(x->BPF_fd, BIOCSETF, &prog) < 0) LogMsg("mDNSPlatformUpdateProxyList: BIOCSETF(%d) failed %d (%s)", prog.bf_len, errno, strerror(errno));
-    else LogSPS("mDNSPlatformUpdateProxyList: BIOCSETF(%d) successful", prog.bf_len);
+    
+    if (ioctl(x->BPF_fd, BIOCSETFNR, &prog) < 0) LogMsg("mDNSPlatformUpdateProxyList: BIOCSETFNR(%d) failed %d (%s)", prog.bf_len, errno, strerror(errno));
+    else LogSPS("mDNSPlatformUpdateProxyList: BIOCSETFNR(%d) successful", prog.bf_len);
 }
 
 mDNSexport void mDNSPlatformReceiveBPF_fd(mDNS *const m, int fd)
 }
 
 mDNSexport void mDNSPlatformReceiveBPF_fd(mDNS *const m, int fd)
@@ -3227,11 +3837,68 @@ mDNSlocal int GetMAC(mDNSEthAddr *eth, u_short ifindex)
 #define ifr_wake_flags ifr_ifru.ifru_intval
 #endif
 
 #define ifr_wake_flags ifr_ifru.ifru_intval
 #endif
 
+mDNSlocal mDNSBool  CheckInterfaceSupport(NetworkInterfaceInfo *const intf, const char *key)
+{
+    io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOBSDNameMatching(kIOMasterPortDefault, 0, intf->ifname));
+    if (!service)
+    {
+        LogSPS("CheckInterfaceSupport: No service for interface %s", intf->ifname);
+        return mDNSfalse;
+    }
+
+    io_name_t n1, n2;
+    IOObjectGetClass(service, n1);
+    io_object_t parent;
+    mDNSBool    ret = mDNSfalse;
+    kern_return_t kr = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent);
+    if (kr == KERN_SUCCESS)
+    {
+        CFStringRef keystr =  CFStringCreateWithCString(NULL, key, kCFStringEncodingUTF8);
+        IOObjectGetClass(parent, n2);
+        LogSPS("CheckInterfaceSupport: Interface %s service %s parent %s", intf->ifname, n1, n2);
+        const CFTypeRef ref = IORegistryEntryCreateCFProperty(parent, keystr, kCFAllocatorDefault, mDNSNULL);
+        if (!ref)
+        {
+            LogSPS("CheckInterfaceSupport: No mDNS_IOREG_KEY for interface %s/%s/%s", intf->ifname, n1, n2);
+            ret = mDNSfalse;
+        }
+        else
+        {
+            ret = mDNStrue;
+            CFRelease(ref);
+        }
+        IOObjectRelease(parent);
+        CFRelease(keystr);
+    }
+    else
+    {
+        LogSPS("CheckInterfaceSupport: IORegistryEntryGetParentEntry for %s/%s failed %d", intf->ifname, n1, kr);
+        ret = mDNSfalse;
+    }
+    IOObjectRelease(service);
+    return ret;
+}
+
+
+mDNSlocal  mDNSBool InterfaceSupportsKeepAlive(NetworkInterfaceInfo *const intf)
+{
+    return CheckInterfaceSupport(intf, mDNS_IOREG_KA_KEY);
+}
+
 mDNSlocal mDNSBool NetWakeInterface(NetworkInterfaceInfoOSX *i)
 {
     if (!MulticastInterface(i)     ) return(mDNSfalse); // We only use Sleep Proxy Service on multicast-capable interfaces
     if (i->ifa_flags & IFF_LOOPBACK) return(mDNSfalse); // except loopback
 
 mDNSlocal mDNSBool NetWakeInterface(NetworkInterfaceInfoOSX *i)
 {
     if (!MulticastInterface(i)     ) return(mDNSfalse); // We only use Sleep Proxy Service on multicast-capable interfaces
     if (i->ifa_flags & IFF_LOOPBACK) return(mDNSfalse); // except loopback
 
+    // If the interface supports TCPKeepalive, it is capable of waking up for a magic packet
+    // This check is needed since the SIOCGIFWAKEFLAGS ioctl returns wrong values for WOMP capability
+    // when the power source is not AC Power.
+    if (InterfaceSupportsKeepAlive(&i->ifinfo))
+    {
+        LogSPS("NetWakeInterface: %s supports TCP Keepalive returning true", i->ifinfo.ifname);
+        return mDNStrue;
+    }
+
     int s = socket(AF_INET, SOCK_DGRAM, 0);
     if (s < 0) { LogMsg("NetWakeInterface socket failed %s error %d errno %d (%s)", i->ifinfo.ifname, s, errno, strerror(errno)); return(mDNSfalse); }
 
     int s = socket(AF_INET, SOCK_DGRAM, 0);
     if (s < 0) { LogMsg("NetWakeInterface socket failed %s error %d errno %d (%s)", i->ifinfo.ifname, s, errno, strerror(errno)); return(mDNSfalse); }
 
@@ -3334,6 +4001,8 @@ mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifad
                     mDNS_Unlock(m);
                 }
             }
                     mDNS_Unlock(m);
                 }
             }
+            // Reset the flag if it has changed this time.
+            (*p)->ifinfo.IgnoreIPv4LL = ((eflags & IFEF_ARPLL) != 0) ? mDNSfalse : mDNStrue;
 
             return(*p);
         }
 
             return(*p);
         }
@@ -3352,6 +4021,7 @@ mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifad
     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->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->ifinfo.IgnoreIPv4LL = ((eflags & IFEF_ARPLL) != 0) ? mDNSfalse : mDNStrue;
 
     i->next            = mDNSNULL;
     i->m               = m;
 
     i->next            = mDNSNULL;
     i->m               = m;
@@ -3359,6 +4029,7 @@ mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifad
     i->Flashing        = mDNSfalse;
     i->Occulting       = mDNSfalse;
     i->D2DInterface    = (eflags & IFEF_LOCALNET_PRIVATE) ? mDNStrue: mDNSfalse;
     i->Flashing        = mDNSfalse;
     i->Occulting       = mDNSfalse;
     i->D2DInterface    = (eflags & IFEF_LOCALNET_PRIVATE) ? mDNStrue: mDNSfalse;
+    i->DirectLink      = (eflags & IFEF_DIRECTLINK) ? mDNStrue: mDNSfalse;
     if (eflags & IFEF_AWDL)
     {
         AWDLInterfaceID = i->ifinfo.InterfaceID;
     if (eflags & IFEF_AWDL)
     {
         AWDLInterfaceID = i->ifinfo.InterfaceID;
@@ -3385,18 +4056,6 @@ mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifad
     return(i);
 }
 
     return(i);
 }
 
-#if USE_V6_ONLY_WHEN_NO_ROUTABLE_V4
-mDNSlocal NetworkInterfaceInfoOSX *FindRoutableIPv4(mDNS *const m, mDNSu32 scope_id)
-{
-    NetworkInterfaceInfoOSX *i;
-    for (i = m->p->InterfaceList; i; i = i->next)
-        if (i->Exists && i->scope_id == scope_id && i->ifinfo.ip.type == mDNSAddrType_IPv4)
-            if (!mDNSv4AddressIsLinkLocal(&i->ifinfo.ip.ip.v4))
-                return(i);
-    return(mDNSNULL);
-}
-#endif
-
 #if APPLE_OSX_mDNSResponder
 
 #if COMPILER_LIKES_PRAGMA_MARK
 #if APPLE_OSX_mDNSResponder
 
 #if COMPILER_LIKES_PRAGMA_MARK
@@ -3465,7 +4124,7 @@ mDNSlocal mStatus UpdateRRStatus(const mDNS *const m, char *buffer, int bufsz, c
             for (ptr = m->AuthInfoList; ptr; ptr = ptr->next)
                 if (SameDomainName(&ptr->domain, n))
                 {
             for (ptr = m->AuthInfoList; ptr; ptr = ptr->next)
                 if (SameDomainName(&ptr->domain, n))
                 {
-                    if (ptr == info && (r->updateError == mStatus_BadSig || r->updateError == mStatus_BadKey))
+                    if (ptr == info && (r->updateError == mStatus_BadSig || r->updateError == mStatus_BadKey || r->updateError == mStatus_BadTime))
                     {
                         mDNS_snprintf(buffer, bufsz, "Resource record update failed for %##s", r->resrec.name);
                         return r->updateError;
                     {
                         mDNS_snprintf(buffer, bufsz, "Resource record update failed for %##s", r->resrec.name);
                         return r->updateError;
@@ -3486,7 +4145,9 @@ mDNSlocal void UpdateAutoTunnelDomainStatus(const mDNS *const m, const DomainAut
         (void) m;
     (void)info;
 #else
         (void) m;
     (void)info;
 #else
-    const NATTraversalInfo *const llq = m->LLQNAT.clientContext ? &m->LLQNAT : mDNSNULL;
+    // Note that in the LLQNAT, the clientCallback being non-zero means it's in use,
+    // whereas in the AutoTunnelNAT, the clientContext being non-zero means it's in use
+    const NATTraversalInfo *const llq = m->LLQNAT.clientCallback ? &m->LLQNAT : mDNSNULL;
     const NATTraversalInfo *const tun = m->AutoTunnelNAT.clientContext ? &m->AutoTunnelNAT : mDNSNULL;
     char buffer[1024];
     mDNSu32 buflen = 0;
     const NATTraversalInfo *const tun = m->AutoTunnelNAT.clientContext ? &m->AutoTunnelNAT : mDNSNULL;
     char buffer[1024];
     mDNSu32 buflen = 0;
@@ -3535,16 +4196,6 @@ mDNSlocal void UpdateAutoTunnelDomainStatus(const mDNS *const m, const DomainAut
         CFRelease(tmp);
     }
 
         CFRelease(tmp);
     }
 
-    mDNS_snprintf(buffer, sizeof(buffer), "%.4a", &m->ExternalAddress);
-    tmp = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8);
-    if (!tmp)
-        LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString ExternalAddress");
-    else
-    {
-        CFDictionarySetValue(dict, CFSTR("ExternalAddress"), tmp);
-        CFRelease(tmp);
-    }
-
     if (llq)
     {
         mDNSu32 port = mDNSVal16(llq->ExternalPort);
     if (llq)
     {
         mDNSu32 port = mDNSVal16(llq->ExternalPort);
@@ -3584,6 +4235,16 @@ mDNSlocal void UpdateAutoTunnelDomainStatus(const mDNS *const m, const DomainAut
             CFRelease(num);
         }
 
             CFRelease(num);
         }
 
+        mDNS_snprintf(buffer, sizeof(buffer), "%.4a", &tun->ExternalAddress);
+        tmp = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8);
+        if (!tmp)
+            LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString ExternalAddress");
+        else
+        {
+            CFDictionarySetValue(dict, CFSTR("ExternalAddress"), tmp);
+            CFRelease(tmp);
+        }
+
         if (tun->Result)
         {
             num = CFNumberCreate(NULL, kCFNumberSInt32Type, &tun->Result);
         if (tun->Result)
         {
             num = CFNumberCreate(NULL, kCFNumberSInt32Type, &tun->Result);
@@ -3648,14 +4309,14 @@ mDNSlocal void UpdateAutoTunnelDomainStatus(const mDNS *const m, const DomainAut
     else if ((llq && llq->Result == mStatus_DoubleNAT) || (tun && tun->Result == mStatus_DoubleNAT))
     {
         status = mStatus_DoubleNAT;
     else if ((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");
+        mDNS_snprintf(buffer, sizeof(buffer), "Double NAT: Router is reporting a private 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)))))
     {
         status = mStatus_NATPortMappingDisabled;
     }
     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)))))
     {
         status = mStatus_NATPortMappingDisabled;
-        mDNS_snprintf(buffer, sizeof(buffer), "NAT-PMP is disabled on the router");
+        mDNS_snprintf(buffer, sizeof(buffer), "PCP/NAT-PMP is disabled on the router");
     }
     else if ((llq && llq->Result) || (tun && tun->Result))
     {
     }
     else if ((llq && llq->Result) || (tun && tun->Result))
     {
@@ -3899,11 +4560,8 @@ mDNSlocal void UpdateAutoTunnelDeviceInfoRecord(mDNS *m, DomainAuthInfo *info)
     {
         mDNS_SetupResourceRecord(&info->AutoTunnelDeviceInfo, mDNSNULL, mDNSInterface_Any, kDNSType_TXT,  kStandardTTL, kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info);
         ConstructServiceName(&info->AutoTunnelDeviceInfo.namestorage, &m->nicelabel, &DeviceInfoName, &info->domain);
     {
         mDNS_SetupResourceRecord(&info->AutoTunnelDeviceInfo, mDNSNULL, mDNSInterface_Any, kDNSType_TXT,  kStandardTTL, kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info);
         ConstructServiceName(&info->AutoTunnelDeviceInfo.namestorage, &m->nicelabel, &DeviceInfoName, &info->domain);
-        mDNSu8 len = m->HIHardware.c[0] < 255 - 6 ? m->HIHardware.c[0] : 255 - 6;
-        mDNSPlatformMemCopy(info->AutoTunnelDeviceInfo.resrec.rdata->u.data + 1, "model=", 6);
-        mDNSPlatformMemCopy(info->AutoTunnelDeviceInfo.resrec.rdata->u.data + 7, m->HIHardware.c + 1, len);
-        info->AutoTunnelDeviceInfo.resrec.rdata->u.data[0] = 6 + len;   // "model=" plus the device string
-        info->AutoTunnelDeviceInfo.resrec.rdlength         = 7 + len;   // One extra for the length byte at the start of the string
+
+        info->AutoTunnelDeviceInfo.resrec.rdlength = initializeDeviceInfoTXT(m, info->AutoTunnelDeviceInfo.resrec.rdata->u.data);
         info->AutoTunnelDeviceInfo.resrec.RecordType = kDNSRecordTypeKnownUnique;
 
         mStatus err = mDNS_Register_internal(m, &info->AutoTunnelDeviceInfo);
         info->AutoTunnelDeviceInfo.resrec.RecordType = kDNSRecordTypeKnownUnique;
 
         mStatus err = mDNS_Register_internal(m, &info->AutoTunnelDeviceInfo);
@@ -4484,10 +5142,13 @@ mDNSexport void AddNewClientTunnel(mDNS *const m, DNSQuestion *const q)
     p->q.RetryWithSearchDomains = mDNSfalse;
     p->q.TimeoutQuestion  = 0;
     p->q.WakeOnResolve    = 0;
     p->q.RetryWithSearchDomains = mDNSfalse;
     p->q.TimeoutQuestion  = 0;
     p->q.WakeOnResolve    = 0;
-    p->q.UseBrackgroundTrafficClass = mDNSfalse;
+    p->q.UseBackgroundTrafficClass = mDNSfalse;
     p->q.ValidationRequired = 0;
     p->q.ValidatingResponse = 0;
     p->q.ValidationRequired = 0;
     p->q.ValidatingResponse = 0;
+    p->q.ProxyQuestion      = 0;
     p->q.qnameOrig        = mDNSNULL;
     p->q.qnameOrig        = mDNSNULL;
+    p->q.AnonInfo         = mDNSNULL;
+    p->q.pid              = mDNSPlatformGetPID();
     p->q.QuestionCallback = AutoTunnelCallback;
     p->q.QuestionContext  = p;
 
     p->q.QuestionCallback = AutoTunnelCallback;
     p->q.QuestionContext  = p;
 
@@ -4510,10 +5171,9 @@ mDNSlocal mStatus UpdateInterfaceList(mDNS *const m, mDNSs32 utc)
     struct ifaddrs *v4Loopback  = NULL;
     struct ifaddrs *v6Loopback  = NULL;
     char defaultname[64];
     struct ifaddrs *v4Loopback  = NULL;
     struct ifaddrs *v6Loopback  = NULL;
     char defaultname[64];
-#ifndef NO_IPV6
     int InfoSocket              = socket(AF_INET6, SOCK_DGRAM, 0);
     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
+    if (InfoSocket < 3 && errno != EAFNOSUPPORT) 
+        LogMsg("UpdateInterfaceList: InfoSocket error %d errno %d (%s)", InfoSocket, errno, strerror(errno));
 
     while (ifa)
     {
 
     while (ifa)
     {
@@ -4578,7 +5238,7 @@ mDNSlocal mStatus UpdateInterfaceList(mDNS *const m, mDNSs32 utc)
                     // <rdar://problem/5492035> getifaddrs is returning invalid netmask family for fw0 and vmnet
                     ifa->ifa_netmask->sa_family = ifa->ifa_addr->sa_family;
                     int ifru_flags6 = 0;
                     // <rdar://problem/5492035> getifaddrs is returning invalid netmask family for fw0 and vmnet
                     ifa->ifa_netmask->sa_family = ifa->ifa_addr->sa_family;
                     int ifru_flags6 = 0;
-#ifndef NO_IPV6
+
                     struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
                     if (ifa->ifa_addr->sa_family == AF_INET6 && InfoSocket >= 0)
                     {
                     struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
                     if (ifa->ifa_addr->sa_family == AF_INET6 && InfoSocket >= 0)
                     {
@@ -4590,23 +5250,25 @@ mDNSlocal mStatus UpdateInterfaceList(mDNS *const m, mDNSs32 utc)
                             ifru_flags6 = ifr6.ifr_ifru.ifru_flags6;
                         verbosedebugf("%s %.16a %04X %04X", ifa->ifa_name, &sin6->sin6_addr, ifa->ifa_flags, ifru_flags6);
                     }
                             ifru_flags6 = ifr6.ifr_ifru.ifru_flags6;
                         verbosedebugf("%s %.16a %04X %04X", ifa->ifa_name, &sin6->sin6_addr, ifa->ifa_flags, ifru_flags6);
                     }
-#endif
+
                     if (!(ifru_flags6 & (IN6_IFF_NOTREADY | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED | IN6_IFF_TEMPORARY)))
                     {
                         if (ifa->ifa_flags & IFF_LOOPBACK)
                         {
                     if (!(ifru_flags6 & (IN6_IFF_NOTREADY | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED | IN6_IFF_TEMPORARY)))
                     {
                         if (ifa->ifa_flags & IFF_LOOPBACK)
                         {
-                            if (ifa->ifa_addr->sa_family == AF_INET) v4Loopback = ifa;
-#ifndef NO_IPV6
-                            else if (sin6->sin6_addr.s6_addr[0] != 0xFD) v6Loopback = ifa;
-#endif
+                            if (ifa->ifa_addr->sa_family == AF_INET) 
+                                v4Loopback = ifa;
+                            else if (sin6->sin6_addr.s6_addr[0] != 0xFD) 
+                                v6Loopback = ifa;
                         }
                         else
                         {
                             NetworkInterfaceInfoOSX *i = AddInterfaceToList(m, ifa, utc);
                             if (i && MulticastInterface(i) && i->ifinfo.Advertise)
                             {
                         }
                         else
                         {
                             NetworkInterfaceInfoOSX *i = AddInterfaceToList(m, ifa, utc);
                             if (i && MulticastInterface(i) && i->ifinfo.Advertise)
                             {
-                                if (ifa->ifa_addr->sa_family == AF_INET) foundav4 = mDNStrue;
-                                else foundav6 = mDNStrue;
+                                if (ifa->ifa_addr->sa_family == AF_INET) 
+                                    foundav4 = mDNStrue;
+                                else 
+                                    foundav6 = mDNStrue;
                             }
                         }
                     }
                             }
                         }
                     }
@@ -4625,9 +5287,6 @@ mDNSlocal mStatus UpdateInterfaceList(mDNS *const m, mDNSs32 utc)
         if (i->Exists)
         {
             mDNSBool txrx = MulticastInterface(i);
         if (i->Exists)
         {
             mDNSBool txrx = MulticastInterface(i);
-#if USE_V6_ONLY_WHEN_NO_ROUTABLE_V4
-            txrx = txrx && ((i->ifinfo.ip.type == mDNSAddrType_IPv4) || !FindRoutableIPv4(m, i->scope_id));
-#endif
             if (i->ifinfo.McastTxRx != txrx)
             {
                 i->ifinfo.McastTxRx = txrx;
             if (i->ifinfo.McastTxRx != txrx)
             {
                 i->ifinfo.McastTxRx = txrx;
@@ -4635,9 +5294,8 @@ mDNSlocal mStatus UpdateInterfaceList(mDNS *const m, mDNSs32 utc)
             }
         }
 
             }
         }
 
-#ifndef NO_IPV6
-    if (InfoSocket >= 0) close(InfoSocket);
-#endif
+    if (InfoSocket >= 0) 
+        close(InfoSocket);
 
     mDNS_snprintf(defaultname, sizeof(defaultname), "%.*s-%02X%02X%02X%02X%02X%02X", HINFO_HWstring_prefixlen, HINFO_HWstring,
                   m->PrimaryMAC.b[0], m->PrimaryMAC.b[1], m->PrimaryMAC.b[2], m->PrimaryMAC.b[3], m->PrimaryMAC.b[4], m->PrimaryMAC.b[5]);
 
     mDNS_snprintf(defaultname, sizeof(defaultname), "%.*s-%02X%02X%02X%02X%02X%02X", HINFO_HWstring_prefixlen, HINFO_HWstring,
                   m->PrimaryMAC.b[0], m->PrimaryMAC.b[1], m->PrimaryMAC.b[2], m->PrimaryMAC.b[3], m->PrimaryMAC.b[4], m->PrimaryMAC.b[5]);
@@ -4724,7 +5382,7 @@ mDNSlocal int SetupActiveInterfaces(mDNS *const m, mDNSs32 utc)
         if (i->Exists)
         {
             NetworkInterfaceInfo *const n = &i->ifinfo;
         if (i->Exists)
         {
             NetworkInterfaceInfo *const n = &i->ifinfo;
-            NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifinfo.ifname, AAAA_OVER_V4 ? AF_UNSPEC : i->sa_family);
+            NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifinfo.ifname, AF_UNSPEC);
             if (!primary) LogMsg("SetupActiveInterfaces ERROR! SearchForInterfaceByName didn't find %s", i->ifinfo.ifname);
 
             if (i->Registered && i->Registered != primary)  // Sanity check
             if (!primary) LogMsg("SetupActiveInterfaces ERROR! SearchForInterfaceByName didn't find %s", i->ifinfo.ifname);
 
             if (i->Registered && i->Registered != primary)  // Sanity check
@@ -4752,9 +5410,10 @@ mDNSlocal int SetupActiveInterfaces(mDNS *const m, mDNSs32 utc)
                 // 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.
                 // 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)
+                // Same logic applies when IFEF_DIRECTLINK flag is set on the interface.
+                if ((strncmp(i->ifinfo.ifname, "p2p", 3) == 0) || i->DirectLink)
                 {
                 {
-                    LogInfo("SetupActiveInterfaces: P2P %s interface registering %s %s", i->ifinfo.ifname,
+                    LogInfo("SetupActiveInterfaces: %s interface registering %s %s", i->ifinfo.ifname,
                             i->Flashing               ? " (Flashing)"  : "",
                             i->Occulting              ? " (Occulting)" : "");
                     mDNS_RegisterInterface(m, n, 0);
                             i->Flashing               ? " (Flashing)"  : "",
                             i->Occulting              ? " (Occulting)" : "");
                     mDNS_RegisterInterface(m, n, 0);
@@ -4806,7 +5465,6 @@ mDNSlocal int SetupActiveInterfaces(mDNS *const m, mDNSs32 utc)
                         if (err < 0 && (errno != EADDRINUSE))
                             LogMsg("setsockopt - IP_ADD_MEMBERSHIP error %d errno %d (%s) group %.4a on %.4a", err, errno, strerror(errno), &imr.imr_multiaddr, &imr.imr_interface);
                     }
                         if (err < 0 && (errno != EADDRINUSE))
                             LogMsg("setsockopt - IP_ADD_MEMBERSHIP error %d errno %d (%s) group %.4a on %.4a", err, errno, strerror(errno), &imr.imr_multiaddr, &imr.imr_interface);
                     }
-#ifndef NO_IPV6
                     if (i->sa_family == AF_INET6)
                     {
                         struct ipv6_mreq i6mr;
                     if (i->sa_family == AF_INET6)
                     {
                         struct ipv6_mreq i6mr;
@@ -4827,7 +5485,6 @@ mDNSlocal int SetupActiveInterfaces(mDNS *const m, mDNSs32 utc)
                         if (err < 0 && (errno != EADDRINUSE))
                             LogMsg("setsockopt - IPV6_JOIN_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface);
                     }
                         if (err < 0 && (errno != EADDRINUSE))
                             LogMsg("setsockopt - IPV6_JOIN_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface);
                     }
-#endif
                 }
             }
         }
                 }
             }
         }
@@ -4858,7 +5515,7 @@ mDNSlocal int ClearInactiveInterfaces(mDNS *const m, mDNSs32 utc)
     for (i = m->p->InterfaceList; i; i = i->next)
     {
         // If this interface is no longer active, or its InterfaceID is changing, deregister it
     for (i = m->p->InterfaceList; i; i = i->next)
     {
         // If this interface is no longer active, or its InterfaceID is changing, deregister it
-        NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifinfo.ifname, AAAA_OVER_V4 ? AF_UNSPEC : i->sa_family);
+        NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifinfo.ifname, AF_UNSPEC);
         if (i->Registered)
             if (i->Exists == 0 || i->Exists == 2 || i->Registered != primary)
             {
         if (i->Registered)
             if (i->Exists == 0 || i->Exists == 2 || i->Registered != primary)
             {
@@ -4875,9 +5532,10 @@ mDNSlocal int ClearInactiveInterfaces(mDNS *const m, mDNSs32 utc)
                 // 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.
                 // 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)
+                // Same logic applies when IFEF_DIRECTLINK flag is set on the interface.
+                if ((strncmp(i->ifinfo.ifname, "p2p", 3) == 0) || i->DirectLink)
                 {
                 {
-                    LogInfo("ClearInactiveInterfaces: P2P %s interface deregistering %s %s", i->ifinfo.ifname,
+                    LogInfo("ClearInactiveInterfaces: %s interface deregistering %s %s", i->ifinfo.ifname,
                             i->Flashing               ? " (Flashing)"  : "",
                             i->Occulting              ? " (Occulting)" : "");
                     mDNS_DeregisterInterface(m, &i->ifinfo, 0);
                             i->Flashing               ? " (Flashing)"  : "",
                             i->Occulting              ? " (Occulting)" : "");
                     mDNS_DeregisterInterface(m, &i->ifinfo, 0);
@@ -5001,346 +5659,405 @@ mDNSlocal void FinalizeSearchDomainHash(mDNS *const m, MD5_CTX *sdc)
     else { LogInfo("FinalizeSearchDomains: The hash is same"); }
 }
 
     else { LogInfo("FinalizeSearchDomains: The hash is same"); }
 }
 
-// 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, MD5_CTX *sdc, mDNSu16 resGroupID)
+mDNSexport const char *DNSScopeToString(mDNSu32 scope)
+{
+    switch (scope)
+    {
+        case kScopeNone:
+            return "Unscoped";
+        case kScopeInterfaceID:
+            return "InterfaceScoped";
+        case kScopeServiceID:
+            return "ServiceScoped";
+        default:
+            return "Unknown";
+    }
+}
+
+mDNSlocal void ConfigSearchDomains(mDNS *const m, dns_resolver_t *resolver, mDNSInterfaceID interface, mDNSu32 scope, MD5_CTX *sdc)
+{
+    const char *scopeString = DNSScopeToString(scope);
+    int j;
+
+    if (scope != kScopeNone)
+    {
+        LogInfo("ConfigSearchDomains: (%s) Ignoring search domain for Interface %p", scopeString, interface);
+        return;
+    }
+    for (j = 0; j < resolver->n_search; j++)
+    {
+        LogInfo("ConfigSearchDomains: (%s) configuring search list %s", scopeString, resolver->search[j]);
+        UpdateSearchDomainHash(m, sdc, resolver->search[j], NULL);
+        mDNS_AddSearchDomain_CString(resolver->search[j], NULL);
+    }
+}
+
+mDNSlocal mDNSInterfaceID ConfigParseInterfaceID(mDNS *const m, mDNSu32 ifindex)
+{
+    NetworkInterfaceInfoOSX *ni;
+    mDNSInterfaceID interface;
+
+    for (ni = m->p->InterfaceList; ni; ni = ni->next)
+    {
+        if (ni->ifinfo.InterfaceID && ni->scope_id == ifindex) 
+            break;
+    }
+    if (ni != NULL) 
+    {
+        interface = ni->ifinfo.InterfaceID;
+    }
+    else
+    {
+        // In rare circumstances, we could potentially hit this case where we cannot parse the InterfaceID
+        // (see <rdar://problem/13214785>). At this point, we still accept the DNS Config from configd 
+        // Note: We currently ack the whole dns configuration and not individual resolvers or DNS servers. 
+        // As the caller is going to ack the configuration always, we have to add all the DNS servers 
+        // in the configuration. Otherwise, we won't have any DNS servers up until the network change.
+
+        LogMsg("ConfigParseInterfaceID: interface specific index %d not found (interface may not be UP)",ifindex);
+
+        // Set the correct interface from configd before passing this to mDNS_AddDNSServer() below
+        interface = (mDNSInterfaceID)(unsigned long)ifindex;
+    }
+    return interface;
+}
+
+mDNSlocal void ConfigNonUnicastResolver(mDNS *const m, dns_resolver_t *r)
+{
+    char *opt = r->options;
+    domainname d; 
+
+    if (opt && !strncmp(opt, "mdns", strlen(opt)))
+    {
+        if (!MakeDomainNameFromDNSNameString(&d, r->domain))
+        { 
+            LogMsg("ConfigNonUnicastResolver: config->resolver bad domain %s", r->domain); 
+            return;
+        }
+        mDNS_AddMcastResolver(m, &d, mDNSInterface_Any, r->timeout);
+    }
+}
+
+mDNSlocal void ConfigDNSServers(mDNS *const m, dns_resolver_t *r, mDNSInterfaceID interface, mDNSu32 scope, mDNSu16 resGroupID)
 {
 {
-    int i, j;
+    int n;
     domainname d;
     domainname d;
-#if DNSINFO_VERSION >= 20091104
-    dns_resolver_t **resolver = scope ? config->scoped_resolver : config->resolver;
-    int nresolvers = scope ? config->n_scoped_resolver : config->n_resolver;
-#else
-    (void) scope; // unused
-    dns_resolver_t **resolver = config->resolver;
-    int nresolvers = config->n_resolver;
+    int serviceID = 0;
+    mDNSBool cellIntf = mDNSfalse;
+    mDNSBool scopedDNS = mDNSfalse;
+    mDNSBool reqA, reqAAAA;
+
+    if (!r->domain || !*r->domain) 
+    {
+        d.c[0] = 0;
+    }
+    else if (!MakeDomainNameFromDNSNameString(&d, r->domain))
+    { 
+        LogMsg("ConfigDNSServers: bad domain %s", r->domain); 
+        return;
+    }
+    // Parse the resolver specific attributes that affects all the DNS servers.
+    if (scope == kScopeInterfaceID)
+    {
+        scopedDNS = mDNStrue;
+    }
+    else if (scope == kScopeServiceID)
+    {
+        serviceID = r->service_identifier;
+    }
+
+#if TARGET_OS_IPHONE
+    cellIntf = (r->reach_flags & kSCNetworkReachabilityFlagsIsWWAN) ? mDNStrue : mDNSfalse;
 #endif
 #endif
+    reqA = (r->flags & DNS_RESOLVER_FLAGS_REQUEST_A_RECORDS ? mDNStrue : mDNSfalse);
+    reqAAAA = (r->flags & DNS_RESOLVER_FLAGS_REQUEST_AAAA_RECORDS ? mDNStrue : mDNSfalse);
 
 
-    if (setsearch && !scope && nresolvers)
+    for (n = 0; n < r->n_nameserver; n++)
     {
     {
-        // Due to the vagaries of Apple's SystemConfiguration and dnsinfo.h APIs, if there are no search domains
-        // listed, then you're supposed to interpret the "domain" field as also being the search domain, but if
-        // there *are* search domains listed, then you're supposed to ignore the "domain" field completely and
-        // instead use the search domain list as the sole authority for what domains to search and in what order
-        // (and the domain from the "domain" field will also appear somewhere in that list).
-        // Also, all search domains get added to the search list for resolver[0], so the domains and/or
-        // search lists for other resolvers in the list need to be ignored.
-        //
-        // Note: Starting DNSINFO_VERSION 20091104, search list is present only in the first resolver (resolver 0).
-        // i.e., n_search for the first resolver is always non-zero. We don't guard it with #ifs for better readability
+        mDNSAddr saddr;
+        DNSServer *s;
 
 
-        if (resolver[0]->n_search == 0)
+        if (r->nameserver[n]->sa_family != AF_INET && r->nameserver[n]->sa_family != AF_INET6)
+            continue;
+        
+        if (SetupAddr(&saddr, r->nameserver[n]))
         {
         {
-            LogInfo("ConfigResolvers: (%s) configuring zeroth domain as search list %s", scope ? "Scoped" : "Non-scoped", resolver[0]->domain);
-            UpdateSearchDomainHash(m, sdc, resolver[0]->domain, NULL);
-            mDNS_AddSearchDomain_CString(resolver[0]->domain, mDNSNULL);
+            LogMsg("ConfigDNSServers: Bad address");
+            continue;
         }
         }
-        else
+        
+        // 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, serviceID, &saddr, r->port ? mDNSOpaque16fromIntVal(r->port) : UnicastDNSPort, scope,
+                              (n == 0 ? (r->timeout ? r->timeout : DEFAULT_UDNS_TIMEOUT) : 0), cellIntf, resGroupID, reqA, reqAAAA, mDNStrue);
+        if (s)
         {
         {
-            for (i = 0; i < resolver[0]->n_search; i++)
-            {
-                LogInfo("ConfigResolvers: (%s) configuring search list %s", scope ? "Scoped" : "Non-scoped", resolver[0]->search[i]);
-                UpdateSearchDomainHash(m, sdc, resolver[0]->search[i], NULL);
-                mDNS_AddSearchDomain_CString(resolver[0]->search[i], mDNSNULL);
-            }
+            LogInfo("ConfigDNSServers(%s): DNS server %#a:%d for domain %##s", DNSScopeToString(scope), &s->addr, mDNSVal16(s->port), d.c);
         }
     }
         }
     }
+}
 
 
-    // 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
-    // is only for names that fall under "apple.com", but that's not correct. Actually the default resolver is
-    // for all names not covered by a more specific resolver (i.e. its domain should be ".", the root domain).
-    //
-    // Note: Starting DNSINFO_VERSION 20091104, domain value of this first resolver (resolver 0) is always NULL.
-    // We don't guard it with #ifs for better readability
-    //
-    if ((nresolvers != 0) && resolver[0]->domain)
-        resolver[0]->domain[0] = 0; // don't stop pointing at the memory, just change the first byte
+// ConfigResolvers is called for different types of resolvers: Unscoped resolver, Interface scope resolver and
+// Service scope resolvers. This is indicated by the scope argument.
+//
+// "resolver" has entries that should only be used for unscoped questions.
+//
+// "scoped_resolver" has entries that should only be used for Interface scoped question i.e., questions that specify an
+// interface index (q->InterfaceID)
+//
+// "service_specific_resolver" has entries that should be used for Service scoped question i.e., questions that specify
+// a service identifier (q->ServiceID)
+//
+mDNSlocal void ConfigResolvers(mDNS *const m, dns_config_t *config, mDNSu32 scope, mDNSBool setsearch, mDNSBool setservers, MD5_CTX *sdc, mDNSu16 resGroupID)
+{
+    int i;
+    dns_resolver_t **resolver;
+    int nresolvers;
+    const char *scopeString = DNSScopeToString(scope);
+    mDNSInterfaceID interface;
 
 
+    switch (scope)
+    {
+        case kScopeNone:
+            resolver = config->resolver;
+            nresolvers = config->n_resolver;
+            break;
+        case kScopeInterfaceID:
+            resolver = config->scoped_resolver;
+            nresolvers = config->n_scoped_resolver;
+            break;
+        case kScopeServiceID:
+            resolver = config->service_specific_resolver;
+            nresolvers = config->n_service_specific_resolver;
+            break;
+        default:
+            return;
+    }
     qsort(resolver, nresolvers, sizeof(dns_resolver_t*), compare_dns_configs);
 
     for (i = 0; i < nresolvers; i++)
     {
     qsort(resolver, nresolvers, sizeof(dns_resolver_t*), compare_dns_configs);
 
     for (i = 0; i < nresolvers; i++)
     {
-        int n;
         dns_resolver_t *r = resolver[i];
         dns_resolver_t *r = resolver[i];
-        mDNSInterfaceID interface = mDNSInterface_Any;
-        int disabled = 0;
-
-        LogInfo("ConfigResolvers: %s resolver[%d] domain %s n_nameserver %d", scope ? "Scoped" : "", i, r->domain, r->n_nameserver);
-
-        // On Tiger, dnsinfo entries for mDNS domains have port 5353, the mDNS port.  Ignore them.
-        // 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)
-        {
-            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;
-        }
 
 
+        LogInfo("ConfigResolvers: %s resolver[%d] domain %s n_nameserver %d", scopeString, i, r->domain, r->n_nameserver);
 
 
-        if (!r->domain || !*r->domain) d.c[0] = 0;
-        else if (!MakeDomainNameFromDNSNameString(&d, r->domain))
-        { LogMsg("ConfigResolvers: config->resolver[%d] bad domain %s", i, r->domain); continue; }
+        interface = mDNSInterface_Any;
 
 
-        // DNS server option parsing
-        if (r->options != NULL)
+        // Parse the interface index 
+        if (r->if_index != 0)
         {
         {
-            char *nextOption = r->options;
-            char *currentOption = NULL;
-            while ((currentOption = strsep(&nextOption, " ")) != NULL && currentOption[0] != 0)
-            {
-                // The option may be in the form of interface=xxx where xxx is an interface name.
-                if (strncmp(currentOption, kInterfaceSpecificOption, sizeof(kInterfaceSpecificOption) - 1) == 0)
-                {
-                    NetworkInterfaceInfoOSX *ni;
-                    char ifname[IF_NAMESIZE+1];
-                    mDNSu32 ifindex = 0;
-                    // If something goes wrong finding the interface, create the server entry anyhow but mark it as disabled.
-                    // This allows us to block these special queries from going out on the wire.
-                    strlcpy(ifname, currentOption + sizeof(kInterfaceSpecificOption)-1, sizeof(ifname));
-                    ifindex = if_nametoindex(ifname);
-                    if (ifindex == 0) { disabled = 1; LogMsg("ConfigResolvers: RegisterSplitDNS interface specific - interface %s not found", ifname); continue; }
-                    LogInfo("ConfigResolvers: interface specific entry: %s on %s (%d)", r->domain, ifname, ifindex);
-                    // Find the interface. Can't use mDNSPlatformInterfaceIDFromInterfaceIndex
-                    // because that will call mDNSMacOSXNetworkChanged if the interface doesn't exist
-                    for (ni = m->p->InterfaceList; ni; ni = ni->next)
-                        if (ni->ifinfo.InterfaceID && ni->scope_id == ifindex) break;
-                    if (ni != NULL) interface = ni->ifinfo.InterfaceID;
-                    if (interface == mDNSNULL)
-                    {
-                        disabled = 1;
-                        LogMsg("ConfigResolvers: RegisterSplitDNS interface specific - index %d (%s) not found", ifindex, ifname);
-                        continue;
-                    }
-                }
-            }
+            interface = ConfigParseInterfaceID(m, r->if_index);
         }
 
         }
 
-        // 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 ((interface == mDNSInterface_Any) && (r->if_index != 0))
+        if (setsearch)
         {
         {
-            NetworkInterfaceInfoOSX *ni;
-            interface = mDNSNULL;
-            for (ni = m->p->InterfaceList; ni; ni = ni->next)
-                if (ni->ifinfo.InterfaceID && ni->scope_id == r->if_index) break;
-            if (ni != NULL) interface = ni->ifinfo.InterfaceID;
-            if (interface == mDNSNULL)
-            {
-                disabled = 1;
-                LogMsg("ConfigResolvers: interface specific index %d not found", r->if_index);
+            ConfigSearchDomains(m, resolver[i], interface, scope, sdc);
+            // Parse other scoped resolvers for search lists
+            if (!setservers) 
                 continue;
                 continue;
-            }
         }
         }
-#endif
 
 
-        if (setsearch)
+        if (r->port == 5353 || r->n_nameserver == 0)
         {
         {
-            // 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);
-                    UpdateSearchDomainHash(m, sdc, resolver[i]->search[j], interface);
-                    mDNS_AddSearchDomain_CString(resolver[i]->search[j], interface);
-                }
-                // Parse other scoped resolvers for search lists
-                if (!setservers) continue;
-            }
+            ConfigNonUnicastResolver(m, r);
         }
         }
+        else
+        {
+            // Each scoped resolver gets its own ID (i.e., they are in their own group) so that responses from the
+            // scoped resolver are not used by other non-scoped or scoped resolvers.
+            if (scope != kScopeNone) 
+                resGroupID++;
 
 
-        // Each scoped resolver gets its own ID (i.e., they are in their own group) so that responses from the
-        // scoped resolver are not used by other non-scoped or scoped resolvers.
-        if (scope) resGroupID++;
+            ConfigDNSServers(m, r, interface, scope, resGroupID);
+        }
+    }
+}
 
 
-        for (n = 0; n < r->n_nameserver; n++)
-            if (r->nameserver[n]->sa_family == AF_INET || r->nameserver[n]->sa_family == AF_INET6)
-            {
-                mDNSAddr saddr;
-                // mDNSAddr saddr = { mDNSAddrType_IPv4, { { { 192, 168, 1, 1 } } } }; // for testing
-                if (SetupAddr(&saddr, r->nameserver[n])) LogMsg("RegisterSplitDNS: bad IP address");
-                else
-                {
-                    mDNSBool cellIntf = mDNSfalse;
-                    mDNSBool scopedDNS = mDNSfalse;
-                    DNSServer *s;
-#if DNSINFO_VERSION >= 20091104
-                    // By setting scoped, this DNSServer can only be picked if the right interfaceID
-                    // 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));
-                    else
-                        scopedDNS = (scope && (r->flags & DNS_RESOLVER_FLAGS_SCOPED)) ? mDNStrue : mDNSfalse;
-#endif
-#if (DNSINFO_VERSION >= 20110420) && TARGET_OS_IPHONE
-                    cellIntf = r->reach_flags & kSCNetworkReachabilityFlagsIsWWAN;
-#endif
-                    // 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), cellIntf, resGroupID);
-                    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);
-                    }
-                }
-            }
+#if APPLE_OSX_mDNSResponder
+mDNSlocal mDNSBool QuestionValidForDNSTrigger(DNSQuestion *q)
+{
+    if (QuerySuppressed(q))
+    {
+        debugf("QuestionValidForDNSTrigger: Suppressed: %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+        return mDNSfalse;
+    }
+    if (mDNSOpaque16IsZero(q->TargetQID))
+    {
+        debugf("QuestionValidForDNSTrigger: Multicast: %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+        return mDNSfalse;
     }
     }
+    // If we answered using LocalOnly records e.g., /etc/hosts, don't consider that a valid response
+    // for trigger.
+    if (q->LOAddressAnswers)
+    {
+        debugf("QuestionValidForDNSTrigger: LocalOnly answers: %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+        return mDNSfalse;
+    }
+    return mDNStrue;
 }
 }
+#endif
 
 
-mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **RegDomains, DNameListElem **BrowseDomains)
+// This function is called if we are not delivering unicast answers to "A" or "AAAA" questions.
+// We set our state appropriately so that if we start receiving answers, trigger the
+// upper layer to retry DNS questions.
+#if APPLE_OSX_mDNSResponder
+mDNSexport void mDNSPlatformUpdateDNSStatus(mDNS *const m, DNSQuestion *q)
 {
 {
-    int i;
-    char buf[MAX_ESCAPED_DOMAIN_NAME];  // Max legal C-string name, including terminating NUL
-    domainname d;
-    MD5_CTX sdc;    // search domain context
-    static mDNSu16 resolverGroupID = 0;
+    if (!QuestionValidForDNSTrigger(q))
+        return;
 
 
-    // Need to set these here because we need to do this even if SCDynamicStoreCreate() or SCDynamicStoreCopyValue() below don't succeed
-    if (fqdn) fqdn->c[0]      = 0;
-    if (RegDomains   ) *RegDomains     = NULL;
-    if (BrowseDomains) *BrowseDomains  = NULL;
+    // Ignore applications that start and stop queries for no reason before we ever talk
+    // to any DNS server.
+    if (!q->triedAllServersOnce)
+    {
+        LogInfo("QuestionValidForDNSTrigger: question %##s (%s) stopped too soon", q->qname.c, DNSTypeName(q->qtype));
+        return;
+    }
+    if (q->qtype == kDNSType_A)
+        m->p->v4answers = 0;
+    if (q->qtype == kDNSType_AAAA)
+        m->p->v6answers = 0;
+    if (!m->p->v4answers || !m->p->v6answers)
+    {
+        LogInfo("mDNSPlatformUpdateDNSStatus: Trigger needed v4 %d, v6 %d, quesiton %##s (%s)", m->p->v4answers, m->p->v6answers, q->qname.c,
+            DNSTypeName(q->qtype));
+    }
+}
+#endif
 
 
-    LogInfo("mDNSPlatformSetDNSConfig:%s%s%s%s%s",
-            setservers    ? " setservers"    : "",
-            setsearch     ? " setsearch"     : "",
-            fqdn          ? " fqdn"          : "",
-            RegDomains    ? " RegDomains"    : "",
-            BrowseDomains ? " BrowseDomains" : "");
+mDNSlocal void AckConfigd(mDNS *const m, dns_config_t *config)
+{
+    mDNS_CheckLock(m);
 
 
-    if (setsearch) MD5_Init(&sdc);
+    // Acking the configuration triggers configd to reissue the reachability queries
+    m->p->DNSTrigger = NonZeroTime(m->timenow);
+    _dns_configuration_ack(config, "com.apple.mDNSResponder");
+}
 
 
-    // Add the inferred address-based configuration discovery domains
-    // (should really be in core code I think, not platform-specific)
-    if (setsearch)
+// If v4q is non-NULL, it means we have received some answers for "A" type questions
+// If v6q is non-NULL, it means we have received some answers for "AAAA" type questions
+#if APPLE_OSX_mDNSResponder
+mDNSexport void mDNSPlatformTriggerDNSRetry(mDNS *const m, DNSQuestion *v4q, DNSQuestion *v6q)
+{
+    mDNSBool trigger = mDNSfalse;
+    mDNSs32 timenow;
+
+    // Don't send triggers too often.
+    // If we have started delivering answers to questions, we should send a trigger
+    // if the time permits. If we are delivering answers, we should set the state
+    // of v4answers/v6answers to 1 and avoid sending a trigger.  But, we don't know
+    // whether the answers that are being delivered currently is for configd or some
+    // other application. If we set the v4answers/v6answers to 1 and not deliver a trigger,
+    // then we won't deliver the trigger later when it is okay to send one as the
+    // "answers" are already set to 1. Hence, don't affect the state of v4answers and
+    // v6answers if we are not delivering triggers.
+    mDNS_Lock(m);
+    timenow = m->timenow;
+    if (m->p->DNSTrigger && (timenow - m->p->DNSTrigger) < DNS_TRIGGER_INTERVAL)
     {
     {
-        struct ifaddrs *ifa = mDNSNULL;
-        struct sockaddr_in saddr;
-        mDNSPlatformMemZero(&saddr, sizeof(saddr));
-        saddr.sin_len = sizeof(saddr);
-        saddr.sin_family = AF_INET;
-        saddr.sin_port = 0;
-        saddr.sin_addr.s_addr = *(in_addr_t *)&m->Router.ip.v4;
+        if (!m->p->v4answers || !m->p->v6answers)
+        {
+            debugf("mDNSPlatformTriggerDNSRetry: not triggering, time since last trigger %d ms, v4ans %d, v6ans %d",
+                (timenow - m->p->DNSTrigger), m->p->v4answers, m->p->v6answers);
+        }
+        mDNS_Unlock(m);
+        return;
+    }
+    mDNS_Unlock(m);
+    if (v4q != NULL && QuestionValidForDNSTrigger(v4q))
+    {
+        int old = m->p->v4answers;
 
 
-        // Don't add any reverse-IP search domains if doing the WAB bootstrap queries would cause dial-on-demand connection initiation
-        if (!AddrRequiresPPPConnection((struct sockaddr *)&saddr)) ifa =  myGetIfAddrs(1);
+        m->p->v4answers = 1;
 
 
-        while (ifa)
+        // If there are IPv4 answers now and previously we did not have
+        // any answers, trigger a DNS change so that reachability
+        // can retry the queries again.
+        if (!old)
         {
         {
-            mDNSAddr a, n;
-            if (ifa->ifa_addr->sa_family == AF_INET &&
-                ifa->ifa_netmask                    &&
-                !(ifa->ifa_flags & IFF_LOOPBACK)    &&
-                !SetupAddr(&a, ifa->ifa_addr)       &&
-                !mDNSv4AddressIsLinkLocal(&a.ip.v4)  )
-            {
-                // Apparently it's normal for the sa_family of an ifa_netmask to sometimes be incorrect, so we explicitly fix it here before calling SetupAddr
-                // <rdar://problem/5492035> getifaddrs is returning invalid netmask family for fw0 and vmnet
-                ifa->ifa_netmask->sa_family = ifa->ifa_addr->sa_family;     // Make sure ifa_netmask->sa_family is set correctly
-                SetupAddr(&n, ifa->ifa_netmask);
-                // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code
-                mDNS_snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa.", a.ip.v4.b[3] & n.ip.v4.b[3],
-                              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]);
-                UpdateSearchDomainHash(m, &sdc, buf, NULL);
-                mDNS_AddSearchDomain_CString(buf, mDNSNULL);
-            }
-            ifa = ifa->ifa_next;
+            LogInfo("mDNSPlatformTriggerDNSRetry: Triggering because of IPv4, last trigger %d ms, %##s (%s)", (timenow - m->p->DNSTrigger),
+                v4q->qname.c, DNSTypeName(v4q->qtype));
+            trigger = mDNStrue;
         }
     }
         }
     }
+    if (v6q != NULL && QuestionValidForDNSTrigger(v6q))
+    {
+        int old = m->p->v6answers;
 
 
-#ifndef MDNS_NO_DNSINFO
-    if (setservers || setsearch)
+        m->p->v6answers = 1;
+        // If there are IPv6 answers now and previously we did not have
+        // any answers, trigger a DNS change so that reachability
+        // can retry the queries again.
+        if (!old)
+        {
+            LogInfo("mDNSPlatformTriggerDNSRetry: Triggering because of IPv6, last trigger %d ms, %##s (%s)", (timenow - m->p->DNSTrigger),
+                v6q->qname.c, DNSTypeName(v6q->qtype));
+            trigger = mDNStrue;
+        }
+    }
+    if (trigger)
     {
         dns_config_t *config = dns_configuration_copy();
     {
         dns_config_t *config = dns_configuration_copy();
-        if (!config)
+        if (config)
         {
         {
-            // When running on 10.3 (build 7xxx) and earlier, we don't expect dns_configuration_copy() to succeed
-            // On 10.4, calls to dns_configuration_copy() early in the boot process often fail.
-            // 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 ((mDNSu32)mDNSPlatformRawTime() > (mDNSu32)(mDNSPlatformOneSecond * 180))
-                LogMsg("mDNSPlatformSetDNSConfig: Error: dns_configuration_copy returned NULL");
+            mDNS_Lock(m);
+            AckConfigd(m, config);
+            mDNS_Unlock(m);
+            dns_configuration_free(config);
         }
         else
         {
         }
         else
         {
-            LogInfo("mDNSPlatformSetDNSConfig: config->n_resolver = %d", config->n_resolver);
+            LogMsg("mDNSPlatformTriggerDNSRetry: ERROR!! configd did not return config");
+        }
+    }
+}
 
 
-#if APPLE_OSX_mDNSResponder
-            // Record the so-called "primary" domain, which we use as a hint to tell if the user is on a network set up
-            // by someone using Microsoft Active Directory using "local" as a private internal top-level domain
-            if (config->n_resolver && config->resolver[0]->domain && config->resolver[0]->n_nameserver && config->resolver[0]->nameserver[0])
-                MakeDomainNameFromDNSNameString(&ActiveDirectoryPrimaryDomain, config->resolver[0]->domain);
-            else ActiveDirectoryPrimaryDomain.c[0] = 0;
-            //MakeDomainNameFromDNSNameString(&ActiveDirectoryPrimaryDomain, "test.local");
-            ActiveDirectoryPrimaryDomainLabelCount = CountLabels(&ActiveDirectoryPrimaryDomain);
-            if (config->n_resolver && config->resolver[0]->n_nameserver && SameDomainName(SkipLeadingLabels(&ActiveDirectoryPrimaryDomain, ActiveDirectoryPrimaryDomainLabelCount - 1), &localdomain))
-                SetupAddr(&ActiveDirectoryPrimaryDomainServer, config->resolver[0]->nameserver[0]);
-            else
-            {
-                AssignDomainName(&ActiveDirectoryPrimaryDomain, (const domainname *)"");
-                ActiveDirectoryPrimaryDomainLabelCount = 0;
-                ActiveDirectoryPrimaryDomainServer = zeroAddr;
-            }
-#endif
+mDNSlocal void SetupActiveDirectoryDomain(dns_config_t *config)
+{
+    // Record the so-called "primary" domain, which we use as a hint to tell if the user is on a network set up
+    // by someone using Microsoft Active Directory using "local" as a private internal top-level domain
+    if (config->n_resolver && config->resolver[0]->domain && config->resolver[0]->n_nameserver &&
+        config->resolver[0]->nameserver[0])
+    {
+        MakeDomainNameFromDNSNameString(&ActiveDirectoryPrimaryDomain, config->resolver[0]->domain);
+    }
+    else
+    {
+         ActiveDirectoryPrimaryDomain.c[0] = 0;
+    }
 
 
-            // With scoped DNS, we don't want to answer a non-scoped question using a scoped cache entry
-            // and vice-versa. As we compare resolverGroupID for matching cache entry with question, we need
-            // to make sure that they don't match. We ensure this by always bumping up resolverGroupID between
-            // the two calls to ConfigResolvers DNSServers for scoped and non-scoped can never have the
-            // same resolverGroupID.
-            //
-            // All non-scoped resolvers use the same resolverGroupID i.e, we treat them all equally.
-            ConfigResolvers(m, config, mDNSfalse, setsearch, setservers, &sdc, ++resolverGroupID);
-            resolverGroupID += config->n_resolver;
-#if DNSINFO_VERSION >= 20091104
-            ConfigResolvers(m, config, mDNStrue, setsearch, setservers, &sdc, resolverGroupID);
-#endif
-            // Acking provides a hint that we processed this current configuration and
-            // we will use that from now on, assuming we don't get another one immediately
-            // after we return from here.
-            _dns_configuration_ack(config, "com.apple.mDNSResponder");
-            dns_configuration_free(config);
-            if (setsearch) FinalizeSearchDomainHash(m, &sdc);
-            setservers = mDNSfalse;  // Done these now -- no need to fetch the same data from SCDynamicStore
-            setsearch  = mDNSfalse;
-        }
+    //MakeDomainNameFromDNSNameString(&ActiveDirectoryPrimaryDomain, "test.local");
+    ActiveDirectoryPrimaryDomainLabelCount = CountLabels(&ActiveDirectoryPrimaryDomain);
+    if (config->n_resolver && config->resolver[0]->n_nameserver &&
+        SameDomainName(SkipLeadingLabels(&ActiveDirectoryPrimaryDomain, ActiveDirectoryPrimaryDomainLabelCount - 1), &localdomain))
+    {
+        SetupAddr(&ActiveDirectoryPrimaryDomainServer, config->resolver[0]->nameserver[0]);
     }
     }
-#endif // MDNS_NO_DNSINFO
+    else
+    {
+        AssignDomainName(&ActiveDirectoryPrimaryDomain, (const domainname *)"");
+        ActiveDirectoryPrimaryDomainLabelCount = 0;
+        ActiveDirectoryPrimaryDomainServer = zeroAddr;
+    }
+}
+#endif
+
+mDNSlocal void SetupDDNSDomains(domainname *const fqdn, DNameListElem **RegDomains, DNameListElem **BrowseDomains)
+{
+    int i;
+    char buf[MAX_ESCAPED_DOMAIN_NAME];  // Max legal C-string name, including terminating NUL
+    domainname d;
 
     SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:mDNSPlatformSetDNSConfig"), NULL, NULL);
     if (!store)
 
     SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:mDNSPlatformSetDNSConfig"), NULL, NULL);
     if (!store)
-        LogMsg("mDNSPlatformSetDNSConfig: SCDynamicStoreCreate failed: %s", SCErrorString(SCError()));
+    {
+        LogMsg("SetupDDNSDomains: SCDynamicStoreCreate failed: %s", SCErrorString(SCError()));
+    }
     else
     {
         CFDictionaryRef ddnsdict = SCDynamicStoreCopyValue(store, NetworkChangedKey_DynamicDNS);
     else
     {
         CFDictionaryRef ddnsdict = SCDynamicStoreCopyValue(store, NetworkChangedKey_DynamicDNS);
@@ -5448,72 +6165,148 @@ mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDN
                 }
                 CFRelease(btmm);
             }
                 }
                 CFRelease(btmm);
             }
-        }
+        }
+        CFRelease(store);
+    }
+}
+
+// Returns mDNSfalse, if it does not set the configuration i.e., if the DNS configuration did not change
+mDNSexport mDNSBool mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn,
+    DNameListElem **RegDomains, DNameListElem **BrowseDomains, mDNSBool ackConfig)
+{
+    MD5_CTX sdc;    // search domain context
+    static mDNSu16 resolverGroupID = 0;
+
+    // Need to set these here because we need to do this even if SCDynamicStoreCreate() or SCDynamicStoreCopyValue() below don't succeed
+    if (fqdn) fqdn->c[0]      = 0;
+    if (RegDomains   ) *RegDomains     = NULL;
+    if (BrowseDomains) *BrowseDomains  = NULL;
+
+    LogInfo("mDNSPlatformSetDNSConfig:%s%s%s%s%s",
+            setservers    ? " setservers"    : "",
+            setsearch     ? " setsearch"     : "",
+            fqdn          ? " fqdn"          : "",
+            RegDomains    ? " RegDomains"    : "",
+            BrowseDomains ? " BrowseDomains" : "");
+
+    if (setsearch) MD5_Init(&sdc);
+
+    // Add the inferred address-based configuration discovery domains
+    // (should really be in core code I think, not platform-specific)
+    if (setsearch)
+    {
+        struct ifaddrs *ifa = mDNSNULL;
+        struct sockaddr_in saddr;
+        mDNSPlatformMemZero(&saddr, sizeof(saddr));
+        saddr.sin_len = sizeof(saddr);
+        saddr.sin_family = AF_INET;
+        saddr.sin_port = 0;
+        saddr.sin_addr.s_addr = *(in_addr_t *)&m->Router.ip.v4;
+
+        // Don't add any reverse-IP search domains if doing the WAB bootstrap queries would cause dial-on-demand connection initiation
+        if (!AddrRequiresPPPConnection((struct sockaddr *)&saddr)) ifa =  myGetIfAddrs(1);
+
+        while (ifa)
+        {
+            mDNSAddr a, n;
+            char buf[64];
+
+            if (ifa->ifa_addr->sa_family == AF_INET &&
+                ifa->ifa_netmask                    &&
+                !(ifa->ifa_flags & IFF_LOOPBACK)    &&
+                !SetupAddr(&a, ifa->ifa_addr)       &&
+                !mDNSv4AddressIsLinkLocal(&a.ip.v4)  )
+            {
+                // Apparently it's normal for the sa_family of an ifa_netmask to sometimes be incorrect, so we explicitly fix it here before calling SetupAddr
+                // <rdar://problem/5492035> getifaddrs is returning invalid netmask family for fw0 and vmnet
+                ifa->ifa_netmask->sa_family = ifa->ifa_addr->sa_family;     // Make sure ifa_netmask->sa_family is set correctly
+                SetupAddr(&n, ifa->ifa_netmask);
+                // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code
+                mDNS_snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa.", a.ip.v4.b[3] & n.ip.v4.b[3],
+                              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]);
+                UpdateSearchDomainHash(m, &sdc, buf, NULL);
+                mDNS_AddSearchDomain_CString(buf, mDNSNULL);
+            }
+            ifa = ifa->ifa_next;
+        }
+    }
+
+#ifndef MDNS_NO_DNSINFO
+    if (setservers || setsearch)
+    {
+        dns_config_t *config = dns_configuration_copy();
+        if (!config)
+        {
+            // When running on 10.3 (build 7xxx) and earlier, we don't expect dns_configuration_copy() to succeed
+            // On 10.4, calls to dns_configuration_copy() early in the boot process often fail.
+            // 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 ((mDNSu32)mDNSPlatformRawTime() > (mDNSu32)(mDNSPlatformOneSecond * 180))
+                LogMsg("mDNSPlatformSetDNSConfig: Error: dns_configuration_copy returned NULL");
+        }
+        else
+        {
+            LogInfo("mDNSPlatformSetDNSConfig: config->n_resolver = %d, generation %llu", config->n_resolver, config->generation);
+            if (m->p->LastConfigGeneration == config->generation)
+            {
+                LogInfo("mDNSPlatformSetDNSConfig: generation number %llu same, not processing", config->generation);
+                dns_configuration_free(config);
+                SetupDDNSDomains(fqdn, RegDomains, BrowseDomains);
+                return mDNSfalse;
+            }
+#if APPLE_OSX_mDNSResponder
+            SetupActiveDirectoryDomain(config);
+#endif
 
 
-        if (setservers || setsearch)
-        {
-            CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_DNS);
-            if (dict)
+            // With scoped DNS, we don't want to answer a non-scoped question using a scoped cache entry
+            // and vice-versa. As we compare resolverGroupID for matching cache entry with question, we need
+            // to make sure that they don't match. We ensure this by always bumping up resolverGroupID between
+            // the two calls to ConfigResolvers DNSServers for scoped and non-scoped can never have the
+            // same resolverGroupID.
+            //
+            // All non-scoped resolvers use the same resolverGroupID i.e, we treat them all equally.
+            ConfigResolvers(m, config, kScopeNone, setsearch, setservers, &sdc, ++resolverGroupID);
+            resolverGroupID += config->n_resolver;
+
+            ConfigResolvers(m, config, kScopeInterfaceID, setsearch, setservers, &sdc, resolverGroupID);
+            resolverGroupID += config->n_scoped_resolver;
+
+            ConfigResolvers(m, config, kScopeServiceID, setsearch, setservers, &sdc, resolverGroupID);
+
+            // Acking provides a hint that we processed this current configuration and
+            // we will use that from now on, assuming we don't get another one immediately
+            // after we return from here.
+            if (ackConfig)
             {
             {
-                if (setservers)
-                {
-                    CFArrayRef values = CFDictionaryGetValue(dict, kSCPropNetDNSServerAddresses);
-                    if (values)
-                    {
-                        LogInfo("DNS Server Address values: %d", (int)CFArrayGetCount(values));
-                        for (i = 0; i < CFArrayGetCount(values); i++)
-                        {
-                            CFStringRef s = CFArrayGetValueAtIndex(values, i);
-                            mDNSAddr addr = { mDNSAddrType_IPv4, { { { 0 } } } };
-                            if (s && CFStringGetCString(s, buf, 256, kCFStringEncodingUTF8) &&
-                                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, 0, mDNSfalse, 0);
-                            }
-                        }
-                    }
-                    else LogInfo("No DNS Server Address values");
-                }
-                if (setsearch)
-                {
-                    // Add the manual and/or DHCP-dicovered search domains
-                    CFArrayRef searchDomains = CFDictionaryGetValue(dict, kSCPropNetDNSSearchDomains);
-                    if (searchDomains)
-                    {
-                        for (i = 0; i < CFArrayGetCount(searchDomains); i++)
-                        {
-                            CFStringRef s = CFArrayGetValueAtIndex(searchDomains, i);
-                            if (s && CFStringGetCString(s, buf, sizeof(buf), kCFStringEncodingUTF8))
-                            {
-                                UpdateSearchDomainHash(m, &sdc, buf, NULL);
-                                mDNS_AddSearchDomain_CString(buf, mDNSNULL);
-                            }
-                        }
-                    }
-                    else    // No kSCPropNetDNSSearchDomains, so use kSCPropNetDNSDomainName
-                    {
-                        // Due to the vagaries of Apple's SystemConfiguration and dnsinfo.h APIs, if there are no search domains
-                        // listed, then you're supposed to interpret the "domain" field as also being the search domain, but if
-                        // there *are* search domains listed, then you're supposed to ignore the "domain" field completely and
-                        // instead use the search domain list as the sole authority for what domains to search and in what order
-                        // (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))
-                        {
-                            UpdateSearchDomainHash(m, &sdc, buf, NULL);
-                            mDNS_AddSearchDomain_CString(buf, mDNSNULL);
-                        }
-                    }
-                    FinalizeSearchDomainHash(m, &sdc);
-                }
-                CFRelease(dict);
+                // Note: We have to set the generation number here when we are acking.
+                // For every DNS configuration change, we do the following:
+                //
+                // 1) Copy dns configuration, handle search domains change
+                // 2) Copy dns configuration, handle dns server change
+                //
+                // If we update the generation number at step (1), we won't process the
+                // DNS servers the second time because generation number would be the same.
+                // As we ack only when we process dns servers, we set the generation number
+                // during acking.
+                m->p->LastConfigGeneration = config->generation;
+                LogInfo("mDNSPlatformSetDNSConfig: Acking configuration setservers %d, setsearch %d", setservers, setsearch);
+                AckConfigd(m, config);
             }
             }
+            dns_configuration_free(config);
+            if (setsearch) FinalizeSearchDomainHash(m, &sdc);
+            setservers = mDNSfalse;  // Done these now -- no need to fetch the same data from SCDynamicStore
+            setsearch  = mDNSfalse;
         }
         }
-        CFRelease(store);
     }
     }
+#endif // MDNS_NO_DNSINFO
+    SetupDDNSDomains(fqdn, RegDomains, BrowseDomains);
+    return mDNStrue;
 }
 
 }
 
+
 mDNSexport mStatus mDNSPlatformGetPrimaryInterface(mDNS *const m, mDNSAddr *v4, mDNSAddr *v6, mDNSAddr *r)
 {
     char buf[256];
 mDNSexport mStatus mDNSPlatformGetPrimaryInterface(mDNS *const m, mDNSAddr *v4, mDNSAddr *v6, mDNSAddr *r)
 {
     char buf[256];
@@ -5996,7 +6789,16 @@ mDNSexport void SetDomainSecrets(mDNS *m)
         }
         CFRelease(secrets);
     }
         }
         CFRelease(secrets);
     }
-    mDNSDynamicStoreSetConfig(kmDNSPrivateConfig, mDNSNULL, sa);
+
+    if (!privateDnsArray || !CFEqual(privateDnsArray, sa))
+    {
+        if (privateDnsArray)
+            CFRelease(privateDnsArray);
+        
+        privateDnsArray = sa;
+        CFRetain(privateDnsArray);
+        mDNSDynamicStoreSetConfig(kmDNSPrivateConfig, mDNSNULL, privateDnsArray);
+    }
     CFRelease(sa);
 
 #if APPLE_OSX_mDNSResponder
     CFRelease(sa);
 
 #if APPLE_OSX_mDNSResponder
@@ -6044,7 +6846,8 @@ mDNSexport void SetDomainSecrets(mDNS *m)
         {
             // stop the NAT operation, reset port, cleanup state
             mDNS_StopNATOperation_internal(m, &m->AutoTunnelNAT);
         {
             // stop the NAT operation, reset port, cleanup state
             mDNS_StopNATOperation_internal(m, &m->AutoTunnelNAT);
-            m->AutoTunnelNAT.ExternalAddress = m->ExternalAddress;
+            m->AutoTunnelNAT.ExternalAddress = zerov4Addr;
+            m->AutoTunnelNAT.NewAddress      = zerov4Addr;
             m->AutoTunnelNAT.ExternalPort    = zeroIPPort;
             m->AutoTunnelNAT.RequestedPort   = zeroIPPort;
             m->AutoTunnelNAT.Lifetime        = 0;
             m->AutoTunnelNAT.ExternalPort    = zeroIPPort;
             m->AutoTunnelNAT.RequestedPort   = zeroIPPort;
             m->AutoTunnelNAT.Lifetime        = 0;
@@ -6288,55 +7091,11 @@ mDNSlocal mDNSs32 GetSystemSleepTimerSetting(void)
     return val;
 }
 
     return val;
 }
 
-#if !TARGET_OS_IPHONE
-mDNSlocal mDNSBool GetInternetSharingStatus(void)
-{
-    mDNSBool ret = mDNSfalse;
-
-       // Currently, we do not have a mechanism to advertise sleep proxy services only on the network on which Internet sharing
-       // is enabled. As a result, we start acting as a very unreliable sleep proxy on other networks when Internet sharing is
-       // turned on.
-       // We should look at ways to advertise only on the relevant network. Till this is done, we should turn off this feature.
-    LogInfo("GetInternetSharingStatus: Disabled");
-    return mDNSfalse;
-
-    SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:mDNSPlatformInternetSharing"), NULL, NULL);
-    if (!store)
-        LogMsg("GetInternetSharingStatus: SCDynamicStoreCreate failed: %s", SCErrorString(SCError()));
-    else
-    {
-        CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_InternetSharing);
-        if (dict)
-        {
-            CFNumberRef state = (CFNumberRef)CFDictionaryGetValue(dict, CFSTR("State"));
-            if (state)
-            {
-                mDNSu32 val;
-                if (CFNumberGetValue(state, kCFNumberSInt32Type, &val))
-                    ret = (val ==  MIS_SVC_STATE_ON) ? mDNStrue :  mDNSfalse;
-                else
-                    LogMsg("GetInternetSharingStatus: CFNumberGetValue error");
-            }
-            CFRelease(dict);
-        }
-        CFRelease(store);
-    }
-    LogInfo("GetInternetSharingStatus: Internet Sharing state %s", (!ret ? "OFF" : "ON"));
-    return ret;
-}
-#else
-// We don't want to support this yet in embedded
-mDNSlocal mDNSBool GetInternetSharingStatus(void)
-{
-    LogInfo("GetInternetSharingStatus: Disabled on embedded");
-    return mDNSfalse;
-}
-#endif
-
 mDNSlocal void SetSPS(mDNS *const m)
 {
 mDNSlocal void SetSPS(mDNS *const m)
 {
-    mDNSu8 sps = GetInternetSharingStatus() ? mDNSSleepProxyMetric_PrimarySoftware :
-                 (OfferSleepProxyService && GetSystemSleepTimerSetting() == 0) ? mDNSSleepProxyMetric_IncidentalSoftware : 0;
+    
+    // If we ever want to know InternetSharing status in the future, use DNSXEnableProxy()
+    mDNSu8 sps = (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)
 
     // 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)
@@ -6360,10 +7119,6 @@ mDNSlocal void SetSPS(mDNS *const m)
 // However, since these definitions can't really be changed without breaking binary compatibility,
 // they should never change, so in practice it should not be a big problem to have them defined here.
 
 // However, since these definitions can't really be changed without breaking binary compatibility,
 // they should never change, so in practice it should not be a big problem to have them defined here.
 
-#define mDNS_IOREG_KEY               "mDNS_KEY"
-#define mDNS_IOREG_VALUE             "2009-07-30"
-#define mDNS_USER_CLIENT_CREATE_TYPE 'mDNS'
-
 enum
 {                               // commands from the daemon to the driver
     cmd_mDNSOffloadRR = 21,     // give the mdns update buffer to the driver
 enum
 {                               // commands from the daemon to the driver
     cmd_mDNSOffloadRR = 21,     // give the mdns update buffer to the driver
@@ -6409,16 +7164,71 @@ mDNSlocal mDNSu16 GetPortArray(mDNS *const m, int trans, mDNSIPPort *portarray)
     return(count);
 }
 
     return(count);
 }
 
+#if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED
+mDNSlocal mDNSBool SupportsTCPKeepAlive()
+{
+    IOReturn  ret      = kIOReturnSuccess;
+    CFTypeRef obj      = NULL;
+    mDNSBool  supports = mDNSfalse;
+
+    ret = IOPlatformCopyFeatureActive(CFSTR("TCPKeepAliveDuringSleep"), &obj);
+    if ((kIOReturnSuccess == ret) && (obj != NULL))
+    {
+        supports = (obj ==  kCFBooleanTrue)? mDNStrue : mDNSfalse;
+        CFRelease(obj);
+    }
+    LogSPS("%s: The hardware %s TCP Keep Alive", __func__, (supports ? "supports" : "does not support"));
+    return supports;
+}
+
+mDNSlocal mDNSBool OnBattery(void)
+{
+    CFTypeRef powerInfo = IOPSCopyPowerSourcesInfo();
+    CFTypeRef powerSrc  = IOPSGetProvidingPowerSourceType(powerInfo);
+    mDNSBool  result    = mDNSfalse;
+
+    if (powerInfo != NULL)
+    {
+        result = CFEqual(CFSTR(kIOPSBatteryPowerValue), powerSrc);
+        CFRelease(powerInfo);
+    }
+    LogSPS("%s: The system is on %s", __func__, (result)? "Battery" : "AC Power");
+    return result;
+}
+
+#endif // !TARGET_OS_EMBEDDED
+
 #define TfrRecordToNIC(RR) \
     ((!(RR)->resrec.InterfaceID && ((RR)->ForceMCast || IsLocalDomain((RR)->resrec.name))))
 
 #define TfrRecordToNIC(RR) \
     ((!(RR)->resrec.InterfaceID && ((RR)->ForceMCast || IsLocalDomain((RR)->resrec.name))))
 
-mDNSlocal mDNSu32 CountProxyRecords(mDNS *const m, uint32_t *const numbytes)
+mDNSlocal mDNSu32 CountProxyRecords(mDNS *const m, uint32_t *const numbytes, NetworkInterfaceInfo *const intf, mDNSBool TCPKAOnly, mDNSBool supportsTCPKA)
 {
     *numbytes = 0;
     int count = 0;
 {
     *numbytes = 0;
     int count = 0;
+
     AuthRecord *rr;
     AuthRecord *rr;
+
     for (rr = m->ResourceRecords; rr; rr=rr->next)
     for (rr = m->ResourceRecords; rr; rr=rr->next)
-        if (rr->resrec.RecordType > kDNSRecordTypeDeregistering)
+    {
+        if (!(rr->AuthFlags & AuthFlagsWakeOnly) && rr->resrec.RecordType > kDNSRecordTypeDeregistering)
+        {
+#if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED
+            mDNSBool   isKeepAliveRecord = mDNS_KeepaliveRecord(&rr->resrec);
+            // Skip over all other records if we are registering TCP KeepAlive records only
+            // Skip over TCP KeepAlive records if the policy prohibits it or if the interface does not support TCP Keepalive.
+            if ((TCPKAOnly && !isKeepAliveRecord) || (isKeepAliveRecord && !supportsTCPKA))
+                continue;
+
+            // Update the record before calculating the number of bytes required
+            // We offload the TCP Keepalive record even if the update fails. When the driver gets the record, it will
+            // attempt to update the record again.
+            if (isKeepAliveRecord && (UpdateKeepaliveRData(m, rr, intf, mDNSfalse, mDNSNULL) != mStatus_NoError))
+                LogSPS("CountProxyRecords: Failed to update keepalive record - %s", ARDisplayString(m, rr));
+#else
+            (void) TCPKAOnly;     // unused
+            (void) supportsTCPKA; // unused
+            (void) intf;          // unused
+#endif // APPLE_OSX_mDNSResponder
             if (TfrRecordToNIC(rr))
             {
                 *numbytes += DomainNameLength(rr->resrec.name) + 10 + rr->resrec.rdestimate;
             if (TfrRecordToNIC(rr))
             {
                 *numbytes += DomainNameLength(rr->resrec.name) + 10 + rr->resrec.rdestimate;
@@ -6426,10 +7236,12 @@ mDNSlocal mDNSu32 CountProxyRecords(mDNS *const m, uint32_t *const numbytes)
                        count, DomainNameLength(rr->resrec.name) + 10 + rr->resrec.rdestimate, *numbytes, ARDisplayString(m,rr));
                 count++;
             }
                        count, DomainNameLength(rr->resrec.name) + 10 + rr->resrec.rdestimate, *numbytes, ARDisplayString(m,rr));
                 count++;
             }
+        }
+    }
     return(count);
 }
 
     return(count);
 }
 
-mDNSlocal void GetProxyRecords(mDNS *const m, DNSMessage *const msg, uint32_t *const numbytes, FatPtr *const records)
+mDNSlocal void GetProxyRecords(mDNS *const m, DNSMessage *const msg, uint32_t *const numbytes, FatPtr *const records, mDNSBool TCPKAOnly, mDNSBool supportsTCPKA)
 {
     mDNSu8 *p = msg->data;
     const mDNSu8 *const limit = p + *numbytes;
 {
     mDNSu8 *p = msg->data;
     const mDNSu8 *const limit = p + *numbytes;
@@ -6437,8 +7249,23 @@ mDNSlocal void GetProxyRecords(mDNS *const m, DNSMessage *const msg, uint32_t *c
 
     int count = 0;
     AuthRecord *rr;
 
     int count = 0;
     AuthRecord *rr;
+
     for (rr = m->ResourceRecords; rr; rr=rr->next)
     for (rr = m->ResourceRecords; rr; rr=rr->next)
-        if (rr->resrec.RecordType > kDNSRecordTypeDeregistering)
+    {
+        if (!(rr->AuthFlags & AuthFlagsWakeOnly) && rr->resrec.RecordType > kDNSRecordTypeDeregistering)
+        {
+#if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED
+            mDNSBool   isKeepAliveRecord = mDNS_KeepaliveRecord(&rr->resrec);
+
+            // Skip over all other records if we are registering TCP KeepAlive records only
+            // Skip over TCP KeepAlive records if the policy prohibits it or if the interface does not support TCP Keepalive
+            if ((TCPKAOnly && !isKeepAliveRecord) || (isKeepAliveRecord && !supportsTCPKA))
+                continue;
+#else
+            (void) TCPKAOnly;     // unused
+            (void) supportsTCPKA; // unused
+#endif // APPLE_OSX_mDNSResponder
+
             if (TfrRecordToNIC(rr))
             {
                 records[count].sixtyfourbits = zeroOpaque64;
             if (TfrRecordToNIC(rr))
             {
                 records[count].sixtyfourbits = zeroOpaque64;
@@ -6451,6 +7278,8 @@ mDNSlocal void GetProxyRecords(mDNS *const m, DNSMessage *const msg, uint32_t *c
                        count, records[count].ptr, p, p - (mDNSu8 *)records[count].ptr, p - msg->data, ARDisplayString(m,rr));
                 count++;
             }
                        count, records[count].ptr, p, p - (mDNSu8 *)records[count].ptr, p - msg->data, ARDisplayString(m,rr));
                 count++;
             }
+        }
+    }
     *numbytes = p - msg->data;
 }
 
     *numbytes = p - msg->data;
 }
 
@@ -6477,35 +7306,61 @@ IOConnectCallStructMethod(
 }
 #endif
 
 }
 #endif
 
-mDNSexport mStatus ActivateLocalProxy(mDNS *const m, char *ifname)  // Called with the lock held
+mDNSexport mDNSBool SupportsInNICProxy(NetworkInterfaceInfo *const intf)
 {
 {
-    mStatus result = mStatus_UnknownErr;
-    io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOBSDNameMatching(kIOMasterPortDefault, 0, ifname));
-    if (!service) { LogMsg("ActivateLocalProxy: No service for interface %s", ifname); return(mStatus_UnknownErr); }
+    if(!UseInternalSleepProxy)
+    {
+        LogSPS("SupportsInNICProxy: Internal Sleep Proxy is disabled");
+        return mDNSfalse;
+    }
+    return CheckInterfaceSupport(intf, mDNS_IOREG_KEY);
+}
+
+mDNSexport mStatus ActivateLocalProxy(mDNS *const m, NetworkInterfaceInfo *const intf)  // Called with the lock held
+{
+    mStatus      result        = mStatus_UnknownErr;
+    mDNSBool     TCPKAOnly     = mDNSfalse;
+    mDNSBool     supportsTCPKA = mDNSfalse;
+    mDNSBool     onbattery     = mDNSfalse; 
+    io_service_t service       = IOServiceGetMatchingService(kIOMasterPortDefault, IOBSDNameMatching(kIOMasterPortDefault, 0, intf->ifname));
+
+#if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED
+    onbattery = OnBattery();
+    // Check if the interface supports TCP Keepalives and the system policy says it is ok to offload TCP Keepalive records
+    supportsTCPKA = (InterfaceSupportsKeepAlive(intf) && SupportsTCPKeepAlive());
+
+    // Only TCP Keepalive records are to be offloaded if
+    // - The system is on battery
+    // - OR wake for network access is not set but powernap is enabled
+    TCPKAOnly     = supportsTCPKA && ((m->SystemWakeOnLANEnabled == mDNS_WakeOnBattery) || onbattery);
+#else
+    (void) onbattery; // unused;
+#endif
+    if (!service) { LogMsg("ActivateLocalProxy: No service for interface %s", intf->ifname); return(mStatus_UnknownErr); }
 
     io_name_t n1, n2;
     IOObjectGetClass(service, n1);
     io_object_t parent;
     kern_return_t kr = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent);
 
     io_name_t n1, n2;
     IOObjectGetClass(service, n1);
     io_object_t parent;
     kern_return_t kr = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent);
-    if (kr != KERN_SUCCESS) LogMsg("ActivateLocalProxy: IORegistryEntryGetParentEntry for %s/%s failed %d", ifname, n1, kr);
+    if (kr != KERN_SUCCESS) LogMsg("ActivateLocalProxy: IORegistryEntryGetParentEntry for %s/%s failed %d", intf->ifname, n1, kr);
     else
     {
         IOObjectGetClass(parent, n2);
     else
     {
         IOObjectGetClass(parent, n2);
-        LogSPS("ActivateLocalProxy: Interface %s service %s parent %s", ifname, n1, n2);
+        LogSPS("ActivateLocalProxy: Interface %s service %s parent %s", intf->ifname, n1, n2);
         const CFTypeRef ref = IORegistryEntryCreateCFProperty(parent, CFSTR(mDNS_IOREG_KEY), kCFAllocatorDefault, mDNSNULL);
         const CFTypeRef ref = IORegistryEntryCreateCFProperty(parent, CFSTR(mDNS_IOREG_KEY), kCFAllocatorDefault, mDNSNULL);
-        if (!ref) LogSPS("ActivateLocalProxy: No mDNS_IOREG_KEY for interface %s/%s/%s", ifname, n1, n2);
+        if (!ref) LogSPS("ActivateLocalProxy: No mDNS_IOREG_KEY for interface %s/%s/%s", intf->ifname, n1, n2);
         else
         {
             if (CFGetTypeID(ref) != CFStringGetTypeID() || !CFEqual(ref, CFSTR(mDNS_IOREG_VALUE)))
                 LogMsg("ActivateLocalProxy: mDNS_IOREG_KEY for interface %s/%s/%s value %s != %s",
         else
         {
             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);
+                       intf->ifname, n1, n2, CFStringGetCStringPtr(ref, mDNSNULL), mDNS_IOREG_VALUE);
             else if (!UseInternalSleepProxy)
             else if (!UseInternalSleepProxy)
-                LogSPS("ActivateLocalProxy: Not using internal (NIC) sleep proxy for interface %s", ifname);
+                LogSPS("ActivateLocalProxy: Not using internal (NIC) sleep proxy for interface %s", intf->ifname);
             else
             {
                 io_connect_t conObj;
                 kr = IOServiceOpen(parent, mach_task_self(), mDNS_USER_CLIENT_CREATE_TYPE, &conObj);
             else
             {
                 io_connect_t conObj;
                 kr = IOServiceOpen(parent, mach_task_self(), mDNS_USER_CLIENT_CREATE_TYPE, &conObj);
-                if (kr != KERN_SUCCESS) LogMsg("ActivateLocalProxy: IOServiceOpen for %s/%s/%s failed %d", ifname, n1, n2, kr);
+                if (kr != KERN_SUCCESS) LogMsg("ActivateLocalProxy: IOServiceOpen for %s/%s/%s failed %d", intf->ifname, n1, n2, kr);
                 else
                 {
                     mDNSOffloadCmd cmd;
                 else
                 {
                     mDNSOffloadCmd cmd;
@@ -6513,7 +7368,7 @@ mDNSexport mStatus ActivateLocalProxy(mDNS *const m, char *ifname)  // Called wi
                     cmd.command       = cmd_mDNSOffloadRR;
                     cmd.numUDPPorts   = GetPortArray(m, mDNSTransport_UDP, mDNSNULL);
                     cmd.numTCPPorts   = GetPortArray(m, mDNSTransport_TCP, mDNSNULL);
                     cmd.command       = cmd_mDNSOffloadRR;
                     cmd.numUDPPorts   = GetPortArray(m, mDNSTransport_UDP, mDNSNULL);
                     cmd.numTCPPorts   = GetPortArray(m, mDNSTransport_TCP, mDNSNULL);
-                    cmd.numRRRecords  = CountProxyRecords(m, &cmd.rrBufferSize);
+                    cmd.numRRRecords  = CountProxyRecords(m, &cmd.rrBufferSize, intf, TCPKAOnly, supportsTCPKA);
                     cmd.compression   = sizeof(DNSMessageHeader);
 
                     DNSMessage *msg = (DNSMessage *)mallocL("mDNSOffloadCmd msg", sizeof(DNSMessageHeader) + cmd.rrBufferSize);
                     cmd.compression   = sizeof(DNSMessageHeader);
 
                     DNSMessage *msg = (DNSMessage *)mallocL("mDNSOffloadCmd msg", sizeof(DNSMessageHeader) + cmd.rrBufferSize);
@@ -6535,13 +7390,13 @@ mDNSexport mStatus ActivateLocalProxy(mDNS *const m, char *ifname)  // Called wi
                                cmd.tcpPorts.ptr, cmd.numTCPPorts);
                     else
                     {
                                cmd.tcpPorts.ptr, cmd.numTCPPorts);
                     else
                     {
-                        GetProxyRecords(m, msg, &cmd.rrBufferSize, cmd.rrRecords.ptr);
+                        GetProxyRecords(m, msg, &cmd.rrBufferSize, cmd.rrRecords.ptr, TCPKAOnly, supportsTCPKA);
                         GetPortArray(m, mDNSTransport_UDP, cmd.udpPorts.ptr);
                         GetPortArray(m, mDNSTransport_TCP, cmd.tcpPorts.ptr);
                         char outputData[2];
                         size_t outputDataSize = sizeof(outputData);
                         kr = IOConnectCallStructMethod(conObj, 0, &cmd, sizeof(cmd), outputData, &outputDataSize);
                         GetPortArray(m, mDNSTransport_UDP, cmd.udpPorts.ptr);
                         GetPortArray(m, mDNSTransport_TCP, cmd.tcpPorts.ptr);
                         char outputData[2];
                         size_t outputDataSize = sizeof(outputData);
                         kr = IOConnectCallStructMethod(conObj, 0, &cmd, sizeof(cmd), outputData, &outputDataSize);
-                        LogSPS("ActivateLocalProxy: IOConnectCallStructMethod for %s/%s/%s %d", ifname, n1, n2, kr);
+                        LogSPS("ActivateLocalProxy: IOConnectCallStructMethod for %s/%s/%s %d", intf->ifname, n1, n2, kr);
                         if (kr == KERN_SUCCESS) result = mStatus_NoError;
                     }
 
                         if (kr == KERN_SUCCESS) result = mStatus_NoError;
                     }
 
@@ -6562,37 +7417,30 @@ mDNSexport mStatus ActivateLocalProxy(mDNS *const m, char *ifname)  // Called wi
 
 #endif // APPLE_OSX_mDNSResponder
 
 
 #endif // APPLE_OSX_mDNSResponder
 
-static io_service_t g_rootdomain = MACH_PORT_NULL;
-
-mDNSlocal mDNSBool SystemWakeForNetworkAccess(void)
+mDNSlocal mDNSu8 SystemWakeForNetworkAccess(void)
 {
     mDNSs32 val = 0;
 {
     mDNSs32 val = 0;
-    CFBooleanRef clamshellStop = NULL;
-    mDNSBool retnow = mDNSfalse;
+    mDNSu8  ret = (mDNSu8)mDNS_NoWake;
 
 
-    if (DisableSleepProxyClient) { LogSPS("SystemWakeForNetworkAccess: Sleep Proxy Client disabled by command-line option"); return mDNSfalse; }
+    if (DisableSleepProxyClient)
+    {
+       LogSPS("SystemWakeForNetworkAccess: Sleep Proxy Client disabled by command-line option");
+       return mDNSfalse;
+    }
 
     GetCurrentPMSetting(CFSTR("Wake On LAN"), &val);
 
     GetCurrentPMSetting(CFSTR("Wake On LAN"), &val);
-    LogSPS("SystemWakeForNetworkAccess: Wake On LAN: %d", val);
-    if (!val) return mDNSfalse;
-
-    if (!g_rootdomain) g_rootdomain = IORegistryEntryFromPath(MACH_PORT_NULL, kIOPowerPlane ":/IOPowerConnection/IOPMrootDomain");
-    if (!g_rootdomain) { LogMsg("SystemWakeForNetworkAccess: IORegistryEntryFromPath failed; assuming no clamshell so can WOMP"); return mDNStrue; }
 
 
-    clamshellStop = (CFBooleanRef)IORegistryEntryCreateCFProperty(g_rootdomain, CFSTR(kAppleClamshellStateKey), kCFAllocatorDefault, 0);
-    if (!clamshellStop) { LogSPS("SystemWakeForNetworkAccess: kAppleClamshellStateKey does not exist; assuming no clamshell so can WOMP"); return mDNStrue; }
-    retnow = clamshellStop == kCFBooleanFalse;
-    CFRelease(clamshellStop);
-    if (retnow) { LogSPS("SystemWakeForNetworkAccess: kAppleClamshellStateKey is false; clamshell is open so can WOMP"); return mDNStrue; }
+    ret = (mDNSu8)(val != 0) ? mDNS_WakeOnAC : mDNS_NoWake;
 
 
-    clamshellStop = (CFBooleanRef)IORegistryEntryCreateCFProperty(g_rootdomain, CFSTR(kAppleClamshellCausesSleepKey), kCFAllocatorDefault, 0);
-    if (!clamshellStop) { LogSPS("SystemWakeForNetworkAccess: kAppleClamshellCausesSleepKey does not exist; assuming no clamshell so can WOMP"); return mDNStrue; }
-    retnow = (clamshellStop == kCFBooleanFalse);
-    CFRelease(clamshellStop);
-    if (retnow) { LogSPS("SystemWakeForNetworkAccess: kAppleClamshellCausesSleepKey is false; clamshell is closed but can WOMP"); return mDNStrue; }
+#if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED
+    // If we have TCP Keepalive support, system is capable of registering for TCP Keepalives.
+    // Further policy decisions on whether to offload the records is handled during sleep processing.
+    if ((ret == mDNS_NoWake) && SupportsTCPKeepAlive())
+        ret = (mDNSu8)mDNS_WakeOnBattery;
+#endif // APPLE_OSX_mDNSResponder
 
 
-    LogSPS("SystemWakeForNetworkAccess: clamshell is closed and can't WOMP");
-    return mDNSfalse;
+    LogSPS("SystemWakeForNetworkAccess: Wake On LAN: %d", ret);
+    return ret;
 }
 
 mDNSlocal mDNSBool SystemSleepOnlyIfWakeOnLAN(void)
 }
 
 mDNSlocal mDNSBool SystemSleepOnlyIfWakeOnLAN(void)
@@ -6904,6 +7752,20 @@ mDNSlocal void ProcessConndConfigChanges(mDNS *const m)
 }
 #endif /* APPLE_OSX_mDNSResponder */
 
 }
 #endif /* APPLE_OSX_mDNSResponder */
 
+mDNSlocal mDNSBool IsAppleNetwork(mDNS *const m)
+{
+    DNSServer *s;
+    // Determine if we're on AppleNW based on DNSServer having 17.x.y.z IPv4 addr
+    for (s = m->DNSServers; s; s = s->next)
+    {
+        if (s->addr.ip.v4.b[0] == 17)
+        {     
+            LogInfo("IsAppleNetwork: Found 17.x.y.z DNSServer concluding that we are on AppleNW: %##s %#a", s->domain.c, &s->addr);
+            return mDNStrue;
+        }     
+    }
+    return mDNSfalse;
+}
 
 mDNSexport void mDNSMacOSXNetworkChanged(mDNS *const m)
 {
 
 mDNSexport void mDNSMacOSXNetworkChanged(mDNS *const m)
 {
@@ -6920,6 +7782,7 @@ mDNSexport void mDNSMacOSXNetworkChanged(mDNS *const m)
     SetupActiveInterfaces(m, utc);
 
 #if APPLE_OSX_mDNSResponder
     SetupActiveInterfaces(m, utc);
 
 #if APPLE_OSX_mDNSResponder
+
     mDNS_Lock(m);
     ProcessConndConfigChanges(m);
     mDNS_Unlock(m);
     mDNS_Lock(m);
     ProcessConndConfigChanges(m);
     mDNS_Unlock(m);
@@ -6989,6 +7852,14 @@ mDNSexport void mDNSMacOSXNetworkChanged(mDNS *const m)
 
     uDNS_SetupDNSConfig(m);
     mDNS_ConfigChanged(m);
 
     uDNS_SetupDNSConfig(m);
     mDNS_ConfigChanged(m);
+
+    if (IsAppleNetwork(m) != mDNS_McastTracingEnabled)
+    {
+        mDNS_McastTracingEnabled = mDNS_McastTracingEnabled ? mDNSfalse : mDNStrue; 
+        LogMsg("mDNSMacOSXNetworkChanged: Multicast Tracing %s", mDNS_McastTracingEnabled ? "Enabled" : "Disabled");
+        UpdateDebugState();
+    }
+
 }
 
 // Called with KQueueLock & mDNS lock
 }
 
 // Called with KQueueLock & mDNS lock
@@ -7162,26 +8033,55 @@ mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, v
     int c2 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_Computername) != 0);
     int c3 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_DynamicDNS  ) != 0);
     int c4 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_DNS         ) != 0);
     int c2 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_Computername) != 0);
     int c3 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_DynamicDNS  ) != 0);
     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
+    if (c && c - c1 - c2 - c3 - c4 == 0)
+        delay = mDNSPlatformOneSecond/10;  // If these were the only changes, shorten delay
 
 
+    // Do immediate network changed processing for "p2p*" interfaces and
+    // for interfaces with the IFEF_DIRECTLINK flag set.
     {
     {
-        int i;
-        for (i=0; i<c; i++)
+        CFArrayRef  labels;
+        CFIndex     n;
+        for (int i = 0; i < c; i++)
         {
         {
-            char buf[256];
-            if (!CFStringGetCString(CFArrayGetValueAtIndex(changedKeys, i), buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0;
-            if (buf[0])
+            CFStringRef key = CFArrayGetValueAtIndex(changedKeys, i);
+
+            // Only look at keys with prefix "State:/Network/Interface/"
+            if (!CFStringHasPrefix(key, NetworkChangedKey_StateInterfacePrefix))
+                continue;
+
+            // And suffix "IPv6" or "IPv4".
+            if (!CFStringHasSuffix(key, kSCEntNetIPv6) && !CFStringHasSuffix(key, kSCEntNetIPv4))
+                continue;
+
+            labels = CFStringCreateArrayBySeparatingStrings(NULL, key, CFSTR("/"));
+            if (labels == NULL)
+                break;
+            n = CFArrayGetCount(labels);
+
+            // Interface changes will have keys of the form: 
+            //     State:/Network/Interface/<interfaceName>/IPv6
+            // Thus five '/' seperated fields, the 4th one being the <interfaceName> string.
+            if (n == 5)
             {
             {
-                if (strstr(buf, "p2p"))
+                char buf[256];
+
+                // The 4th label (index = 3) should be the interface name.
+                if (CFStringGetCString(CFArrayGetValueAtIndex(labels, 3), buf, sizeof(buf), kCFStringEncodingUTF8)
+                    && (strstr(buf, "p2p") || (getExtendedFlags(buf) & IFEF_DIRECTLINK)))
                 {
                 {
-                    LogInfo("NetworkChanged SC key: %s, not delaying network change", buf);
+                    LogInfo("NetworkChanged: interface %s, not delaying network change", buf);
                     changeNow = mDNStrue;
                     changeNow = mDNStrue;
+                    CFRelease(labels);
                     break;
                 }
             }
                     break;
                 }
             }
+            CFRelease(labels);
         }
     }
 
         }
     }
 
+    mDNSBool btmmChanged = CFArrayContainsValue(changedKeys, range, NetworkChangedKey_BackToMyMac);
+    if (btmmChanged) delay = 0;
+
     if (mDNS_LoggingEnabled)
     {
         int i;
     if (mDNS_LoggingEnabled)
     {
         int i;
@@ -7197,13 +8097,11 @@ mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, v
                 c2 ? "(Computer Name) "  : "",
                 c3 ? "(DynamicDNS) "     : "",
                 c4 ? "(DNS) "            : "",
                 c2 ? "(Computer Name) "  : "",
                 c3 ? "(DynamicDNS) "     : "",
                 c4 ? "(DNS) "            : "",
-                delay);
+                changeNow ? 0 : delay);
     }
 
     }
 
-    mDNSBool btmmChanged = CFArrayContainsValue(changedKeys, range, NetworkChangedKey_BackToMyMac);
-    if (btmmChanged) delay = 0;
-
-    SetNetworkChanged(m, delay);
+    if (!changeNow)
+        SetNetworkChanged(m, delay);
 
     // 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
 
     // 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
@@ -7230,7 +8128,8 @@ mDNSlocal void RefreshSPSStatus(const void *key, const void *value, void *contex
 
     CFStringRef ifnameStr = (CFStringRef)key;
     CFArrayRef array = (CFArrayRef)value;
 
     CFStringRef ifnameStr = (CFStringRef)key;
     CFArrayRef array = (CFArrayRef)value;
-    if (!CFStringGetCString(ifnameStr, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0;
+    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);
 
     LogInfo("RefreshSPSStatus: Updating SPS state for key %s, array count %d", buf, CFArrayGetCount(array));
     mDNSDynamicStoreSetConfig(kmDNSSleepProxyServersState, buf, value);
@@ -7256,31 +8155,18 @@ mDNSlocal void DynamicStoreReconnected(SCDynamicStoreRef store, void *info)
     // so that if things are done differently in the future, this code still works.
 
     // State:/Network/PrivateDNS
     // 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);
-    }
+    if (privateDnsArray)
+        mDNSDynamicStoreSetConfig(kmDNSPrivateConfig, mDNSNULL, privateDnsArray);
 
 
-    // State:/Network/BackToMyMac
 #if APPLE_OSX_mDNSResponder
     mDNS_Lock(m);
 #if APPLE_OSX_mDNSResponder
     mDNS_Lock(m);
+    // State:/Network/BackToMyMac
     UpdateAutoTunnelDomainStatuses(m);
     mDNS_Unlock(m);
 
     // State:/Network/Interface/en0/SleepProxyServers
     UpdateAutoTunnelDomainStatuses(m);
     mDNS_Unlock(m);
 
     // State:/Network/Interface/en0/SleepProxyServers
-    if (spsStatusDict) CFDictionaryApplyFunction(spsStatusDict, RefreshSPSStatus, NULL);
+    if (spsStatusDict) 
+        CFDictionaryApplyFunction(spsStatusDict, RefreshSPSStatus, NULL);
 #endif
 }
 
 #endif
 }
 
@@ -7306,7 +8192,6 @@ mDNSlocal mStatus WatchForNetworkChanges(mDNS *const m)
     CFArrayAppendValue(keys, NetworkChangedKey_BackToMyMac);
     CFArrayAppendValue(keys, NetworkChangedKey_PowerSettings); // should remove as part of <rdar://problem/6751656>
     CFArrayAppendValue(keys, NetworkChangedKey_BTMMConnectivity);
     CFArrayAppendValue(keys, NetworkChangedKey_BackToMyMac);
     CFArrayAppendValue(keys, NetworkChangedKey_PowerSettings); // should remove as part of <rdar://problem/6751656>
     CFArrayAppendValue(keys, NetworkChangedKey_BTMMConnectivity);
-    CFArrayAppendValue(keys, NetworkChangedKey_InternetSharing);
     CFArrayAppendValue(patterns, pattern1);
     CFArrayAppendValue(patterns, pattern2);
     CFArrayAppendValue(patterns, CFSTR("State:/Network/Interface/[^/]+/AirPort"));
     CFArrayAppendValue(patterns, pattern1);
     CFArrayAppendValue(patterns, pattern2);
     CFArrayAppendValue(patterns, CFSTR("State:/Network/Interface/[^/]+/AirPort"));
@@ -7376,7 +8261,8 @@ mDNSlocal void mDNSSetPacketFilterRules(mDNS *const m, char * ifname, const Reso
 
     for (rr = m->ResourceRecords; rr; rr=rr->next)
     {
 
     for (rr = m->ResourceRecords; rr; rr=rr->next)
     {
-        if ((rr->resrec.rrtype == kDNSServiceType_SRV) && (rr->ARType == AuthRecordAnyIncludeP2P))
+        if ((rr->resrec.rrtype == kDNSServiceType_SRV) 
+            && ((rr->ARType == AuthRecordAnyIncludeP2P) || (rr->ARType == AuthRecordAnyIncludeAWDLandP2P)))
         {
             const mDNSu8    *p;
 
         {
             const mDNSu8    *p;
 
@@ -7491,7 +8377,8 @@ mDNSlocal void newMasterElected(mDNS *const m, struct net_event_data * ptr)
 
     for (rr = m->ResourceRecords; rr; rr=rr->next)
     {
 
     for (rr = m->ResourceRecords; rr; rr=rr->next)
     {
-        if ((!rr->resrec.InterfaceID && (rr->ARType == AuthRecordAnyIncludeAWDL))
+        if ((!rr->resrec.InterfaceID 
+            && ((rr->ARType == AuthRecordAnyIncludeAWDL) || ((rr->ARType == AuthRecordAnyIncludeAWDLandP2P))))
            || rr->resrec.InterfaceID == InterfaceID)
         {
             LogInfo("newMasterElected: restarting %s announcements for %##s", DNSTypeName(rr->resrec.rrtype), rr->namestorage.c);
            || rr->resrec.InterfaceID == InterfaceID)
         {
             LogInfo("newMasterElected: restarting %s announcements for %##s", DNSTypeName(rr->resrec.rrtype), rr->namestorage.c);
@@ -7506,6 +8393,16 @@ mDNSlocal mDNSBool allZeroSSTH(struct opaque_presence_indication *op)
     int i;
     int *intp = (int *) op->ssth;
 
     int i;
     int *intp = (int *) op->ssth;
 
+    // MAX_SSTH_SIZE should always be a multiple of sizeof(int), if
+    // it's not, print an error message and return false so that
+    // corresponding peer records are not flushed when KEV_DL_NODE_PRESENCE event
+    // is received.
+    if (MAX_SSTH_SIZE % sizeof(int))
+    {
+        LogInfo("allZeroSSTH: MAX_SSTH_SIZE = %d not a multiple of sizeof(int)", MAX_SSTH_SIZE);
+        return mDNSfalse;
+    }
+
     for (i = 0; i < (int)(MAX_SSTH_SIZE / sizeof(int)); i++, intp++)
     {
         if (*intp)
     for (i = 0; i < (int)(MAX_SSTH_SIZE / sizeof(int)); i++, intp++)
     {
         if (*intp)
@@ -7514,6 +8411,10 @@ mDNSlocal mDNSBool allZeroSSTH(struct opaque_presence_indication *op)
     return mDNStrue;
 }
 
     return mDNStrue;
 }
 
+// mDNS_Reconfirm_internal() adds 33% to this interval, so the records should
+// be removed in 4 seconds.
+#define kAWDLReconfirmTime ((mDNSu32)mDNSPlatformOneSecond *  3)
+
 // Mark records from this peer for deletion from the cache.
 mDNSlocal void removeCachedPeerRecords(mDNS *const m, mDNSu32 ifindex, mDNSAddr *ap)
 {
 // Mark records from this peer for deletion from the cache.
 mDNSlocal void removeCachedPeerRecords(mDNS *const m, mDNSu32 ifindex, mDNSAddr *ap)
 {
@@ -7534,7 +8435,7 @@ mDNSlocal void removeCachedPeerRecords(mDNS *const m, mDNSu32 ifindex, mDNSAddr
         {
             LogInfo("removeCachedPeerRecords: %s %##s marking for deletion",
                  DNSTypeName(cr->resrec.rrtype), cr->resrec.name->c);
         {
             LogInfo("removeCachedPeerRecords: %s %##s marking for deletion",
                  DNSTypeName(cr->resrec.rrtype), cr->resrec.name->c);
-            mDNS_PurgeCacheResourceRecord(m, cr);
+            mDNS_Reconfirm_internal(m, cr, kAWDLReconfirmTime);
         }
     }
 }
         }
     }
 }
@@ -7552,12 +8453,7 @@ mDNSlocal void nodePresence(mDNS *const m, struct kev_dl_node_presence * p)
  
     // AWDL will generate a KEV_DL_NODE_PRESENCE event with SSTH field of
     // all zeroes when a node is present and has no services registered.
  
     // AWDL will generate a KEV_DL_NODE_PRESENCE event with SSTH field of
     // all zeroes when a node is present and has no services registered.
-    // We should have already flushed the cache when the previous
-    // KEV_DL_NODE_ABSENCE was received or upon receipt of the goodbye packets
-    // when the service was deregistered.  Thus, the following
-    // cache flush logic is disabled by mDNSHandlePeerEvents being false
-    // until we determine that a cache flush is required on receipt of this event.
-    if (m->mDNSHandlePeerEvents && allZeroSSTH(op))
+    if (allZeroSSTH(op))
     {
         mDNSAddr    peerAddr;
 
     {
         mDNSAddr    peerAddr;
 
@@ -7638,7 +8534,7 @@ mDNSlocal void SysEventCallBack(int s1, short __unused filter, void *context)
 
         if (msg.k.event_code == KEV_DL_MASTER_ELECTED)
             newMasterElected(m, (struct net_event_data *) &msg.k.event_data);
 
         if (msg.k.event_code == KEV_DL_MASTER_ELECTED)
             newMasterElected(m, (struct net_event_data *) &msg.k.event_data);
-            
+
         // We receive network change notifications both through configd and through SYSPROTO_EVENT socket.
         // Configd may not generate network change events for manually configured interfaces (i.e., non-DHCP)
         // always during sleep/wakeup due to some race conditions (See radar:8666757). At the same time, if
         // We receive network change notifications both through configd and through SYSPROTO_EVENT socket.
         // Configd may not generate network change events for manually configured interfaces (i.e., non-DHCP)
         // always during sleep/wakeup due to some race conditions (See radar:8666757). At the same time, if
@@ -7784,10 +8680,18 @@ 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
     {
         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) 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 if (!strncasecmp(HINFO_HWstring, "J33AP", 5)) LogSPS("PowerChanged PowerOn %d seconds late, device is J33AP so not re-sleeping", 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 (IsAppleTV())
+        { 
+            LogSPS("PowerChanged PowerOn %d seconds late, device is an AppleTV running iOS so not re-sleeping", utc - m->p->WakeAtUTC);
+        }
         else
         {
             LogSPS("PowerChanged: Waking for network maintenance operations %d seconds early; re-sleeping in 20 seconds", m->p->WakeAtUTC - utc);
         else
         {
             LogSPS("PowerChanged: Waking for network maintenance operations %d seconds early; re-sleeping in 20 seconds", m->p->WakeAtUTC - utc);
@@ -7839,18 +8743,6 @@ mDNSlocal void PowerChanged(void *refcon, io_service_t service, natural_t messag
     case kIOMessageSystemWillRestart:       LogSPS("PowerChanged kIOMessageSystemWillRestart     (no action)"); break;          // E0000310
     case kIOMessageSystemWillPowerOn:       LogSPS("PowerChanged kIOMessageSystemWillPowerOn");                                 // E0000320
 
     case kIOMessageSystemWillRestart:       LogSPS("PowerChanged kIOMessageSystemWillRestart     (no action)"); break;          // E0000310
     case kIOMessageSystemWillPowerOn:       LogSPS("PowerChanged kIOMessageSystemWillPowerOn");                                 // E0000320
 
-        // 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 < 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);
-        }
-
         // Make sure our interface list is cleared to the empty state, then tell mDNSCore to wake
         if (m->SleepState != SleepState_Sleeping)
         {
         // Make sure our interface list is cleared to the empty state, then tell mDNSCore to wake
         if (m->SleepState != SleepState_Sleeping)
         {
@@ -8600,7 +9492,7 @@ mDNSlocal void UpdateEtcHosts(mDNS *const m, void *context)
 {
     AuthHash *newhosts = (AuthHash *)context;
 
 {
     AuthHash *newhosts = (AuthHash *)context;
 
-    if (!m->mDNS_busy) LogMsg("UpdateEtcHosts: ERROR!! Lock not held");
+    mDNS_CheckLock(m);
 
     //Delete old entries from the core if they are not present in the newhosts
     EtcHostsDeleteOldEntries(m, newhosts, mDNSfalse);
 
     //Delete old entries from the core if they are not present in the newhosts
     EtcHostsDeleteOldEntries(m, newhosts, mDNSfalse);
@@ -8710,11 +9602,7 @@ CF_EXPORT const CFStringRef _kCFSystemVersionProductNameKey;
 CF_EXPORT const CFStringRef _kCFSystemVersionProductVersionKey;
 CF_EXPORT const CFStringRef _kCFSystemVersionBuildVersionKey;
 
 CF_EXPORT const CFStringRef _kCFSystemVersionProductVersionKey;
 CF_EXPORT const CFStringRef _kCFSystemVersionBuildVersionKey;
 
-// Major version  6 is 10.2.x (Jaguar)
-// Major version  7 is 10.3.x (Panther)
-// Major version  8 is 10.4.x (Tiger)
-// Major version  9 is 10.5.x (Leopard)
-// Major version 10 is 10.6.x (SnowLeopard)
+// Major version 13 is 10.9.x 
 mDNSexport void mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring)
 {
     int major = 0, minor = 0;
 mDNSexport void mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring)
 {
     int major = 0, minor = 0;
@@ -8725,18 +9613,28 @@ mDNSexport void mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring)
         CFStringRef cfprodname = CFDictionaryGetValue(vers, _kCFSystemVersionProductNameKey);
         CFStringRef cfprodvers = CFDictionaryGetValue(vers, _kCFSystemVersionProductVersionKey);
         CFStringRef cfbuildver = CFDictionaryGetValue(vers, _kCFSystemVersionBuildVersionKey);
         CFStringRef cfprodname = CFDictionaryGetValue(vers, _kCFSystemVersionProductNameKey);
         CFStringRef cfprodvers = CFDictionaryGetValue(vers, _kCFSystemVersionProductVersionKey);
         CFStringRef cfbuildver = CFDictionaryGetValue(vers, _kCFSystemVersionBuildVersionKey);
-        if (cfprodname) CFStringGetCString(cfprodname, prodname, sizeof(prodname), kCFStringEncodingUTF8);
-        if (cfprodvers) CFStringGetCString(cfprodvers, prodvers, sizeof(prodvers), kCFStringEncodingUTF8);
+        if (cfprodname) 
+            CFStringGetCString(cfprodname, prodname, sizeof(prodname), kCFStringEncodingUTF8);
+        if (cfprodvers) 
+            CFStringGetCString(cfprodvers, prodvers, sizeof(prodvers), kCFStringEncodingUTF8);
         if (cfbuildver && CFStringGetCString(cfbuildver, buildver, sizeof(buildver), kCFStringEncodingUTF8))
             sscanf(buildver, "%d%c%d", &major, &letter, &minor);
         CFRelease(vers);
     }
         if (cfbuildver && CFStringGetCString(cfbuildver, buildver, sizeof(buildver), kCFStringEncodingUTF8))
             sscanf(buildver, "%d%c%d", &major, &letter, &minor);
         CFRelease(vers);
     }
-    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));
+    if (!major) 
+    { 
+        major = 13; 
+        LogMsg("Note: No Major Build Version number found; assuming 13"); 
+    }
+    if (HINFO_SWstring) 
+        mDNS_snprintf(HINFO_SWstring, 256, "%s %s (%s), %s", prodname, prodvers, buildver, STRINGIFY(mDNSResponderVersion));
     //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;
     //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;
+    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.
 }
 
 // Test to see if we're the first client running on UDP port 5353, by trying to bind to 5353 without using SO_REUSEPORT.
@@ -8843,6 +9741,34 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m)
     char HINFO_SWstring[256] = "";
     mDNSMacOSXSystemBuildNumber(HINFO_SWstring);
 
     char HINFO_SWstring[256] = "";
     mDNSMacOSXSystemBuildNumber(HINFO_SWstring);
 
+    err = mDNSHelperInit();
+    if (err)
+        return err;
+    
+    DynamicStoreQueue = dispatch_queue_create("com.apple.mDNSResponder.DynamicStoreQueue", NULL);
+    if (DynamicStoreQueue == NULL)
+    {
+        LogMsg("dispatch_queue_create: DynamicStoreQueue NULL!");
+        return mStatus_NoMemoryErr;
+    }
+
+    // Store mDNSResponder Platform 
+    if (OSXVers)
+    {
+        m->mDNS_plat = platform_OSX;
+    }
+    else if (iOSVers)
+    {
+        if (IsAppleTV())
+            m->mDNS_plat = platform_Atv;
+        else
+            m->mDNS_plat = platform_iOS;
+    }
+    else
+    {
+        m->mDNS_plat = platform_NonApple; 
+    }   
+        
     // 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.
     int i;
     // 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.
     int i;
@@ -8872,9 +9798,8 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m)
     // 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);
 
     // 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;
+    if (mDNSPlatformInit_CanReceiveUnicast()) 
+        m->CanReceiveUnicastOn5353 = mDNStrue;
 
     mDNSu32 hlen = mDNSPlatformStrLen(HINFO_HWstring);
     mDNSu32 slen = mDNSPlatformStrLen(HINFO_SWstring);
 
     mDNSu32 hlen = mDNSPlatformStrLen(HINFO_HWstring);
     mDNSu32 slen = mDNSPlatformStrLen(HINFO_SWstring);
@@ -8892,23 +9817,21 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m)
     m->p->permanentsockets.kqsv4.KQcallback = myKQSocketCallBack;
     m->p->permanentsockets.kqsv4.KQcontext  = &m->p->permanentsockets;
     m->p->permanentsockets.kqsv4.KQtask     = "UDP packet reception";
     m->p->permanentsockets.kqsv4.KQcallback = myKQSocketCallBack;
     m->p->permanentsockets.kqsv4.KQcontext  = &m->p->permanentsockets;
     m->p->permanentsockets.kqsv4.KQtask     = "UDP packet reception";
-#ifndef NO_IPV6
     m->p->permanentsockets.sktv6 = -1;
     m->p->permanentsockets.kqsv6.KQcallback = myKQSocketCallBack;
     m->p->permanentsockets.kqsv6.KQcontext  = &m->p->permanentsockets;
     m->p->permanentsockets.kqsv6.KQtask     = "UDP packet reception";
     m->p->permanentsockets.sktv6 = -1;
     m->p->permanentsockets.kqsv6.KQcallback = myKQSocketCallBack;
     m->p->permanentsockets.kqsv6.KQcontext  = &m->p->permanentsockets;
     m->p->permanentsockets.kqsv6.KQtask     = "UDP packet reception";
-#endif
 
     err = SetupSocket(&m->p->permanentsockets, MulticastDNSPort, AF_INET, mDNSNULL);
 
     err = SetupSocket(&m->p->permanentsockets, MulticastDNSPort, AF_INET, mDNSNULL);
-#ifndef NO_IPV6
     err = SetupSocket(&m->p->permanentsockets, MulticastDNSPort, AF_INET6, mDNSNULL);
     err = SetupSocket(&m->p->permanentsockets, MulticastDNSPort, AF_INET6, mDNSNULL);
-#endif
 
     struct sockaddr_in s4;
     socklen_t n4 = sizeof(s4);
 
     struct sockaddr_in s4;
     socklen_t n4 = sizeof(s4);
-    if (getsockname(m->p->permanentsockets.sktv4, (struct sockaddr *)&s4, &n4) < 0) LogMsg("getsockname v4 error %d (%s)", errno, strerror(errno));
-    else m->UnicastPort4.NotAnInteger = s4.sin_port;
-#ifndef NO_IPV6
+    if (getsockname(m->p->permanentsockets.sktv4, (struct sockaddr *)&s4, &n4) < 0) 
+        LogMsg("getsockname v4 error %d (%s)", errno, strerror(errno));
+    else 
+        m->UnicastPort4.NotAnInteger = s4.sin_port;
+
     if (m->p->permanentsockets.sktv6 >= 0)
     {
         struct sockaddr_in6 s6;
     if (m->p->permanentsockets.sktv6 >= 0)
     {
         struct sockaddr_in6 s6;
@@ -8916,7 +9839,6 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m)
         if (getsockname(m->p->permanentsockets.sktv6, (struct sockaddr *)&s6, &n6) < 0) LogMsg("getsockname v6 error %d (%s)", errno, strerror(errno));
         else m->UnicastPort6.NotAnInteger = s6.sin6_port;
     }
         if (getsockname(m->p->permanentsockets.sktv6, (struct sockaddr *)&s6, &n6) < 0) LogMsg("getsockname v6 error %d (%s)", errno, strerror(errno));
         else m->UnicastPort6.NotAnInteger = s6.sin6_port;
     }
-#endif
 
     m->p->InterfaceList      = mDNSNULL;
     m->p->userhostlabel.c[0] = 0;
 
     m->p->InterfaceList      = mDNSNULL;
     m->p->userhostlabel.c[0] = 0;
@@ -8929,6 +9851,13 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m)
     m->p->KeyChainTimer      = 0;
     m->p->WakeAtUTC          = 0;
     m->p->RequestReSleep     = 0;
     m->p->KeyChainTimer      = 0;
     m->p->WakeAtUTC          = 0;
     m->p->RequestReSleep     = 0;
+    // Assume that everything is good to begin with. If something is not working,
+    // we will detect that when we start sending questions.
+    m->p->v4answers          = 1;
+    m->p->v6answers          = 1;
+    m->p->DNSTrigger         = 0;
+    m->p->LastConfigGeneration = 0;
+
 #if APPLE_OSX_mDNSResponder
     uuid_generate(m->asl_uuid);
 #endif
 #if APPLE_OSX_mDNSResponder
     uuid_generate(m->asl_uuid);
 #endif
@@ -8940,7 +9869,8 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m)
     NetworkChangedKey_Hostnames    = SCDynamicStoreKeyCreateHostNames(NULL);
     NetworkChangedKey_Computername = SCDynamicStoreKeyCreateComputerName(NULL);
     NetworkChangedKey_DNS          = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetDNS);
     NetworkChangedKey_Hostnames    = SCDynamicStoreKeyCreateHostNames(NULL);
     NetworkChangedKey_Computername = SCDynamicStoreKeyCreateComputerName(NULL);
     NetworkChangedKey_DNS          = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetDNS);
-    if (!NetworkChangedKey_IPv4 || !NetworkChangedKey_IPv6 || !NetworkChangedKey_Hostnames || !NetworkChangedKey_Computername || !NetworkChangedKey_DNS)
+    NetworkChangedKey_StateInterfacePrefix = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, CFSTR(""), NULL);
+    if (!NetworkChangedKey_IPv4 || !NetworkChangedKey_IPv6 || !NetworkChangedKey_Hostnames || !NetworkChangedKey_Computername || !NetworkChangedKey_DNS || !NetworkChangedKey_StateInterfacePrefix)
     { LogMsg("SCDynamicStore string setup failed"); return(mStatus_NoMemoryErr); }
 
     err = WatchForNetworkChanges(m);
     { LogMsg("SCDynamicStore string setup failed"); return(mStatus_NoMemoryErr); }
 
     err = WatchForNetworkChanges(m);
@@ -9025,16 +9955,13 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m)
     else if (!strncasecmp(HINFO_HWstring, "Macmini",      7)) { SPMetricPortability = 33 /*  5kg */; SPMetricMarginalPower = 73 /*  20W */; SPMetricTotalPower = 74 /*  25W */; }
     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, "Macmini",      7)) { SPMetricPortability = 33 /*  5kg */; SPMetricMarginalPower = 73 /*  20W */; SPMetricTotalPower = 74 /*  25W */; }
     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, "J33AP",        5)) { SPMetricPortability = 35 /*  3kg */; SPMetricMarginalPower = 60 /*   1W */; SPMetricTotalPower = 63 /*   2W */; }
+    else if (  IsAppleTV()  )                                 { 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 Features %d",
            HINFO_HWstring_prefixlen, HINFO_HWstring, HINFO_HWstring, SPMetricPortability, SPMetricMarginalPower, SPMetricTotalPower, SPMetricFeatures);
 #endif // APPLE_OSX_mDNSResponder
 
     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 Features %d",
            HINFO_HWstring_prefixlen, HINFO_HWstring, HINFO_HWstring, SPMetricPortability, SPMetricMarginalPower, SPMetricTotalPower, SPMetricFeatures);
 #endif // APPLE_OSX_mDNSResponder
 
-#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__
     // 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__
@@ -9043,10 +9970,11 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m)
     SSLqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 #endif
     if (SSLqueue == mDNSNULL) LogMsg("dispatch_queue_create: SSL queue NULL");
     SSLqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 #endif
     if (SSLqueue == mDNSNULL) LogMsg("dispatch_queue_create: SSL queue NULL");
-#endif
+
     mDNSMacOSXUpdateEtcHosts(m);
     SetupLocalHostRecords(m);
     mDNSMacOSXUpdateEtcHosts(m);
     SetupLocalHostRecords(m);
-    m->TrustAnchors = mDNSNULL;
+    CUPInit(m);
+
     return(mStatus_NoError);
 }
 
     return(mStatus_NoError);
 }
 
@@ -9262,9 +10190,15 @@ mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, co
         IOPMAssertionRelease(m->p->IOPMAssertion);
         m->p->IOPMAssertion = 0;
     }
         IOPMAssertionRelease(m->p->IOPMAssertion);
         m->p->IOPMAssertion = 0;
     }
-    else if (!allowSleep && m->p->IOPMAssertion == 0)
+    else if (!allowSleep)
     {
 #ifdef kIOPMAssertionTypeNoIdleSleep
     {
 #ifdef kIOPMAssertionTypeNoIdleSleep
+        if (m->p->IOPMAssertion)
+        {
+            IOPMAssertionRelease(m->p->IOPMAssertion);
+            m->p->IOPMAssertion = 0;
+        }
+
         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);
         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);
@@ -9326,14 +10260,14 @@ mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const Ne
     // If it's an AWDL interface the record must be explicitly marked to include AWDL.
     if (intf->InterfaceID == AWDLInterfaceID)
     {
     // If it's an AWDL interface the record must be explicitly marked to include AWDL.
     if (intf->InterfaceID == AWDLInterfaceID)
     {
-        if (rr->ARType == AuthRecordAnyIncludeAWDL)
+        if (rr->ARType == AuthRecordAnyIncludeAWDL || rr->ARType == AuthRecordAnyIncludeAWDLandP2P)
             return mDNStrue;
         else
             return mDNSfalse;
     }
     
             return mDNStrue;
         else
             return mDNSfalse;
     }
     
-    // Sent record if it is explicitly marked to include all other P2P type interfaces.
-    if (rr->ARType == AuthRecordAnyIncludeP2P)
+    // Send record if it is explicitly marked to include all other P2P type interfaces.
+    if (rr->ARType == AuthRecordAnyIncludeP2P || rr->ARType == AuthRecordAnyIncludeAWDLandP2P)
         return mDNStrue;
 
     // Don't send the record over this interface.
         return mDNStrue;
 
     // Don't send the record over this interface.
@@ -9397,79 +10331,73 @@ mDNSexport void mDNSPlatformFormatTime(unsigned long te, mDNSu8 *buf, int bufsiz
     strftime((char *)buf, bufsize, "%Y%m%d%H%M%S", &tmTime);
 }
 
     strftime((char *)buf, bufsize, "%Y%m%d%H%M%S", &tmTime);
 }
 
-mDNSexport mStatus mDNSPlatformRetrieveTCPInfo(mDNS *const m, mDNSAddr *laddr, mDNSIPPort *lport, mDNSAddr *raddr, mDNSIPPort *rport, mDNSTCPInfo *mti)
+mDNSexport mDNSs32 mDNSPlatformGetPID()
 {
 {
-    struct tcp_info ti;
-    struct info_tuple itpl;
-    int mib[4];
-    unsigned int miblen;
-    size_t len;
-    size_t sz;
-
-    itpl.itpl_proto = IPPROTO_TCP;
-
-    if (laddr->type == mDNSAddrType_IPv4)
-    {
-        mDNSPlatformMemCopy(&itpl.itpl_local_sin.sin_addr, &laddr->ip.v4, sizeof(struct in_addr));
-        mDNSPlatformMemCopy(&itpl.itpl_remote_sin.sin_addr, &raddr->ip.v4, sizeof(struct in_addr));
-        itpl.itpl_local_sin.sin_port = lport->NotAnInteger;
-        itpl.itpl_remote_sin.sin_port = rport->NotAnInteger;
-        itpl.itpl_remote_sin.sin_family = AF_INET;
-    }
-    else
-    {
-        mDNSPlatformMemCopy(&itpl.itpl_local_sin6.sin6_addr, &laddr->ip.v6, sizeof(struct in6_addr));
-        mDNSPlatformMemCopy(&itpl.itpl_remote_sin6.sin6_addr, &raddr->ip.v6, sizeof(struct in6_addr));
-        itpl.itpl_local_sin6.sin6_port = lport->NotAnInteger;
-        itpl.itpl_remote_sin6.sin6_port = rport->NotAnInteger;
-        itpl.itpl_remote_sin6.sin6_family = AF_INET6;
-    }
-
+    return getpid();
+}
 
 
-    sz = sizeof(mib)/sizeof(mib[0]);
-    if (sysctlnametomib("net.inet.tcp.info", mib, &sz) == -1)
-    {
-        LogMsg("mDNSPlatformRetrieveTCPInfo: sysctlnametomib failed %d, %s", errno, strerror(errno));
-        return mStatus_UnknownErr;
-    }
+// Schedule a function asynchronously on the main queue
+mDNSexport void mDNSPlatformDispatchAsync(mDNS *const m, void *context, AsyncDispatchFunc func)
+{
+    // KQueueLock/Unlock is used for two purposes
+    //
+    // 1. We can't be running along with the KQueue thread and hence acquiring the lock
+    //    serializes the access to the "core"
+    //
+    // 2. KQueueUnlock also sends a message wake up the KQueue thread which in turn wakes
+    //    up and calls udsserver_idle which schedules the messages across the uds socket.
+    //    If "func" delivers something to the uds socket from the dispatch thread, it will
+    //    not be delivered immediately if not for the Unlock.
+    dispatch_async(dispatch_get_main_queue(), ^{
+        KQueueLock(m);
+        func(m, context);
+        KQueueUnlock(m, "mDNSPlatformDispatchAsync");
+#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
+        // KQueueUnlock is a noop. Hence, we need to run kick off the idle loop
+        // to handle any message that "func" might deliver.
+        TriggerEventCompletion();
+#endif
+    });
+}
 
 
-    miblen = (unsigned int)sz;
+// definitions for device-info record construction
+#define DEVINFO_MODEL       "model="
+#define DEVINFO_MODEL_LEN   strlen(DEVINFO_MODEL)
 
 
-    len = sizeof(struct tcp_info);
-    if (sysctl(mib, miblen, &ti, &len, &itpl, sizeof(struct info_tuple)) == -1)
-    {
-        LogMsg("mDNSPlatformRetrieveTCPInfo: sysctl failed %d, %s", errno, strerror(errno));
-        return mStatus_UnknownErr;
-    }
-    mti->seq = ti.tcpi_snd_nxt - 1;
-    mti->ack = ti.tcpi_rcv_nxt;
-    mti->window = ti.tcpi_rcv_space >> ti.tcpi_rcv_wscale;
-    mti->IntfId = mDNSPlatformInterfaceIDfromInterfaceIndex(m,  ti.tcpi_last_outif);
-    if (mti->IntfId == mDNSInterface_LocalOnly || mti->IntfId == mDNSInterface_P2P || mti->IntfId == mDNSInterface_Any)
-    {
-        LogMsg("mDNSPlatformRetrieveTCPInfo: Bad InterfaceId %p", mti->IntfId);
-        return mStatus_BadParamErr;
-    }
+#define OSX_VER         "osxvers="
+#define OSX_VER_LEN     strlen(OSX_VER) 
+#define VER_NUM_LEN     2  // 2 digits of version number added to base string
 
 
-    return mStatus_NoError;
-}
+// Bytes available in TXT record for model name after subtracting space for other 
+// fixed size strings and their length bytes.
+#define MAX_MODEL_NAME_LEN   (256 - (DEVINFO_MODEL_LEN + 1) - (OSX_VER_LEN + VER_NUM_LEN + 1))
 
 
-#if APPLE_OSX_mDNSResponder
-mDNSexport void mDNSPlatformToggleInterfaceAdvt(mDNS *const m, mDNSBool stopAdvt)
+// Initialize device-info TXT record contents and return total length of record data.
+mDNSexport mDNSu32 initializeDeviceInfoTXT(mDNS *m, mDNSu8 *ptr)
 {
 {
-    NetworkInterfaceInfoOSX *intf;
+    mDNSu8 *bufferStart = ptr;
+    mDNSu8 len = m->HIHardware.c[0] < MAX_MODEL_NAME_LEN ? m->HIHardware.c[0] : MAX_MODEL_NAME_LEN;
 
 
-    LogInfo("%s called to %s advertisements", __func__, stopAdvt? "stop" : "start");
-    for (intf = m->p->InterfaceList; intf; intf = intf->next)
+    *ptr = DEVINFO_MODEL_LEN + len; // total length of DEVINFO_MODEL string plus the hardware name string
+    ptr++;
+    mDNSPlatformMemCopy(ptr, DEVINFO_MODEL, DEVINFO_MODEL_LEN);
+    ptr += DEVINFO_MODEL_LEN;
+    mDNSPlatformMemCopy(ptr, m->HIHardware.c + 1, len);
+    ptr += len;
+
+    // only include this string for OSX
+    if (OSXVers)
     {
     {
-        // For now, the ioctl only supports suppression of IPv6 advertisements
-        if (intf->ifinfo.InterfaceActive && intf->ifinfo.McastTxRx)
-        {
-            if (mDNSInterfaceAdvtIoctl(intf->ifinfo.ifname, (stopAdvt? 0 : 1)) < 0)
-            {
-                LogMsg("%s: mDNSInterfaceAdvtIoctl failed");
-            }
-        }
+        char    ver_num[VER_NUM_LEN + 1]; // version digits + null written by snprintf
+        *ptr = OSX_VER_LEN + VER_NUM_LEN; // length byte
+        ptr++;
+        mDNSPlatformMemCopy(ptr, OSX_VER, OSX_VER_LEN);
+        ptr += OSX_VER_LEN;
+        // convert version number to ASCII, add 1 for terminating null byte written by snprintf()
+        snprintf(ver_num, VER_NUM_LEN + 1, "%d", OSXVers);
+        mDNSPlatformMemCopy(ptr, ver_num, VER_NUM_LEN);
+        ptr += VER_NUM_LEN;
     }
     }
+
+    return (ptr - bufferStart);
 }
 }
-#endif
index 7c30207aab9078a6f3b47023eb148f56c3ad0331..00bfb87c87093ee6a3cfafb3f4fc2a0a3f762e49 100644 (file)
@@ -15,8 +15,8 @@
  * limitations under the License.
  */
 
  * limitations under the License.
  */
 
-#ifndef __mDNSOSX_h
-#define __mDNSOSX_h
+#ifndef __mDNSMacOSX_h
+#define __mDNSMacOSX_h
 
 #ifdef  __cplusplus
 extern "C" {
 
 #ifdef  __cplusplus
 extern "C" {
@@ -30,11 +30,43 @@ extern "C" {
 #include <netinet/in.h>
 #include "mDNSEmbeddedAPI.h"  // for domain name structure
 
 #include <netinet/in.h>
 #include "mDNSEmbeddedAPI.h"  // for domain name structure
 
+#include <net/if.h>
+
 //#define MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 #include <dispatch/dispatch.h>
 //#define MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 #include <dispatch/dispatch.h>
+#include <dispatch/private.h>
+#endif
+
+#if TARGET_OS_EMBEDDED
+#define NO_SECURITYFRAMEWORK 1
+#define NO_CFUSERNOTIFICATION 1
+#include <MobileGestalt.h> // for IsAppleTV() 
+#include <SystemConfiguration/scprefs_observer.h> // for _scprefs_observer_watch()
+extern mDNSBool GetmDNSManagedPref(CFStringRef key);
+#endif
+
+#ifndef NO_SECURITYFRAMEWORK
+#include <Security/SecureTransport.h>
+#include <Security/Security.h>
+#endif /* NO_SECURITYFRAMEWORK */
+
+#if TARGET_OS_IPHONE
+#include "cellular_usage_policy.h"
 #endif
 
 #endif
 
+#define kmDNSResponderServName "com.apple.mDNSResponder"
+
+enum mDNSDynamicStoreSetConfigKey
+{
+    kmDNSMulticastConfig = 1,
+    kmDNSDynamicConfig,
+    kmDNSPrivateConfig,
+    kmDNSBackToMyMacConfig,
+    kmDNSSleepProxyServersState,
+    kmDNSDebugState, 
+};
+
 typedef struct NetworkInterfaceInfoOSX_struct NetworkInterfaceInfoOSX;
 
 typedef void (*KQueueEventCallback)(int fd, short filter, void *context);
 typedef struct NetworkInterfaceInfoOSX_struct NetworkInterfaceInfoOSX;
 
 typedef void (*KQueueEventCallback)(int fd, short filter, void *context);
@@ -42,7 +74,7 @@ typedef struct
 {
     KQueueEventCallback KQcallback;
     void                *KQcontext;
 {
     KQueueEventCallback KQcallback;
     void                *KQcontext;
-    const char const    *KQtask;        // For debugging messages
+    const char          *KQtask;        // For debugging messages
 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
     dispatch_source_t readSource;
     dispatch_source_t writeSource;
 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
     dispatch_source_t readSource;
     dispatch_source_t writeSource;
@@ -57,11 +89,10 @@ typedef struct
     mDNS                    *m;
     int sktv4;
     KQueueEntry kqsv4;
     mDNS                    *m;
     int sktv4;
     KQueueEntry kqsv4;
-#ifndef NO_IPV6
     int sktv6;
     KQueueEntry kqsv6;
     int sktv6;
     KQueueEntry kqsv6;
-#endif
     int                     *closeFlag;
     int                     *closeFlag;
+    mDNSBool proxy;
 } KQSocketSet;
 
 struct UDPSocket_struct
 } KQSocketSet;
 
 struct UDPSocket_struct
@@ -69,6 +100,36 @@ struct UDPSocket_struct
     KQSocketSet ss;     // First field of KQSocketSet has to be mDNSIPPort -- mDNSCore requires every UDPSocket_struct to begin with mDNSIPPort port
 };
 
     KQSocketSet ss;     // First field of KQSocketSet has to be mDNSIPPort -- mDNSCore requires every UDPSocket_struct to begin with mDNSIPPort port
 };
 
+// TCP socket support
+
+typedef enum
+{
+    handshake_required,
+    handshake_in_progress,
+    handshake_completed,
+    handshake_to_be_closed
+} handshakeStatus;
+
+struct TCPSocket_struct
+{
+    TCPSocketFlags flags;       // MUST BE FIRST FIELD -- mDNSCore expects every TCPSocket_struct to begin with TCPSocketFlags flags
+    TCPConnectionCallback callback;
+    int fd;
+    KQueueEntry *kqEntry;
+    KQSocketSet ss;
+#ifndef NO_SECURITYFRAMEWORK
+    SSLContextRef tlsContext;
+    pthread_t handshake_thread;
+#endif /* NO_SECURITYFRAMEWORK */
+    domainname hostname;
+    void *context;
+    mDNSBool setup;
+    mDNSBool connected;
+    handshakeStatus handshake;
+    mDNS *m; // So we can call KQueueLock from the SSLHandshake thread
+    mStatus err;
+};
+
 struct NetworkInterfaceInfoOSX_struct
 {
     NetworkInterfaceInfo ifinfo;                // MUST be the first element in this structure
 struct NetworkInterfaceInfoOSX_struct
 {
     NetworkInterfaceInfo ifinfo;                // MUST be the first element in this structure
@@ -80,6 +141,7 @@ struct NetworkInterfaceInfoOSX_struct
     mDNSu8 Occulting;                           // Set if interface vanished for less than 60 seconds and then came back
     mDNSu8 D2DInterface;                        // IFEF_LOCALNET_PRIVATE flag indicates we should call
                                                 // D2D plugin for operations over this interface
     mDNSu8 Occulting;                           // Set if interface vanished for less than 60 seconds and then came back
     mDNSu8 D2DInterface;                        // IFEF_LOCALNET_PRIVATE flag indicates we should call
                                                 // D2D plugin for operations over this interface
+    mDNSu8 DirectLink;                          // IFEF_DIRECTLINK flag is set for interface
 
     mDNSs32 AppearanceTime;                     // Time this interface appeared most recently in getifaddrs list
                                                 // i.e. the first time an interface is seen, AppearanceTime is set.
 
     mDNSs32 AppearanceTime;                     // Time this interface appeared most recently in getifaddrs list
                                                 // i.e. the first time an interface is seen, AppearanceTime is set.
@@ -145,20 +207,23 @@ struct mDNS_PlatformSupport_struct
 #endif
     mDNSs32 BigMutexStartTime;
     int WakeKQueueLoopFD;
 #endif
     mDNSs32 BigMutexStartTime;
     int WakeKQueueLoopFD;
+    mDNSu8 v4answers;                  // non-zero if we are receiving answers
+    mDNSu8 v6answers;                  // for A/AAAA from external DNS servers
+    mDNSs32 DNSTrigger;                // Time the DNSTrigger was given
+    uint64_t LastConfigGeneration;     // DNS configuration generation number
+    UDPSocket UDPProxy;
+    TCPSocket TCPProxy;
+    ProxyCallback *UDPProxyCallback;
+    ProxyCallback *TCPProxyCallback;
+#if TARGET_OS_IPHONE
+    cellular_usage_policy_client_t handle;
+#endif
 };
 
 extern int OfferSleepProxyService;
 extern int DisableSleepProxyClient;
 extern int UseInternalSleepProxy;
 extern int OSXVers, iOSVers;
 };
 
 extern int OfferSleepProxyService;
 extern int DisableSleepProxyClient;
 extern int UseInternalSleepProxy;
 extern int OSXVers, iOSVers;
-#define OSXVers_Base              4
-#define OSXVers_10_0_Cheetah      4
-#define OSXVers_10_1_Puma         5
-#define OSXVers_10_2_Jaguar       6
-#define OSXVers_10_3_Panther      7
-#define OSXVers_10_4_Tiger        8
-#define OSXVers_10_5_Leopard      9
-#define OSXVers_10_6_SnowLeopard 10
 
 extern int KQueueFD;
 
 
 extern int KQueueFD;
 
@@ -168,6 +233,9 @@ extern void mDNSMacOSXNetworkChanged(mDNS *const m);
 extern void mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring);
 extern NetworkInterfaceInfoOSX *IfindexToInterfaceInfoOSX(const mDNS *const m, mDNSInterfaceID ifindex);
 extern void mDNSUpdatePacketFilter(const ResourceRecord *const excludeRecord);
 extern void mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring);
 extern NetworkInterfaceInfoOSX *IfindexToInterfaceInfoOSX(const mDNS *const m, mDNSInterfaceID ifindex);
 extern void mDNSUpdatePacketFilter(const ResourceRecord *const excludeRecord);
+extern void myKQSocketCallBack(int s1, short filter, void *context);
+extern void mDNSDynamicStoreSetConfig(int key, const char *subkey, CFPropertyListRef value);
+extern void UpdateDebugState(void);
 
 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 extern int KQueueSet(int fd, u_short flags, short filter, KQueueEntry *const entryRef);
 
 #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM
 extern int KQueueSet(int fd, u_short flags, short filter, KQueueEntry *const entryRef);
@@ -179,12 +247,15 @@ extern int KQueueSet(int fd, u_short flags, short filter, const KQueueEntry *con
 // When events are processed on the non-kqueue thread (i.e. CFRunLoop notifications like Sleep/Wake,
 // Interface changes, Keychain changes, etc.) they must use KQueueLock/KQueueUnlock to lock out the kqueue thread
 extern void KQueueLock(mDNS *const m);
 // When events are processed on the non-kqueue thread (i.e. CFRunLoop notifications like Sleep/Wake,
 // Interface changes, Keychain changes, etc.) they must use KQueueLock/KQueueUnlock to lock out the kqueue thread
 extern void KQueueLock(mDNS *const m);
-extern void KQueueUnlock(mDNS *const m, const char const *task);
+extern void KQueueUnlock(mDNS *const m, const chartask);
 extern void mDNSPlatformCloseFD(KQueueEntry *kq, int fd);
 extern void mDNSPlatformCloseFD(KQueueEntry *kq, int fd);
+extern ssize_t myrecvfrom(const int s, void *const buffer, const size_t max,
+                             struct sockaddr *const from, size_t *const fromlen, mDNSAddr *dstaddr, char *ifname, mDNSu8 *ttl);
 
 extern mDNSBool DictionaryIsEnabled(CFDictionaryRef dict);
 
 
 extern mDNSBool DictionaryIsEnabled(CFDictionaryRef dict);
 
-extern void mDNSPlatformToggleInterfaceAdvt(mDNS *const m, mDNSBool stopAdvt);
+extern void CUPInit(mDNS *const m);
+extern const char *DNSScopeToString(mDNSu32 scope);
 
 // If any event takes more than WatchDogReportingThreshold milliseconds to be processed, we log a warning message
 // General event categories are:
 
 // If any event takes more than WatchDogReportingThreshold milliseconds to be processed, we log a warning message
 // General event categories are:
@@ -209,8 +280,12 @@ struct CompileTimeAssertionChecks_mDNSMacOSX
     // 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.
     // 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_NetworkInterfaceInfoOSX[(sizeof(NetworkInterfaceInfoOSX) <=  7000) ? 1 : -1];
-    char sizecheck_mDNS_PlatformSupport   [(sizeof(mDNS_PlatformSupport)    <=   768) ? 1 : -1];
+
+    // Checks commented out when sizeof(DNSQuestion) change cascaded into having to change yet another
+    // set of hardcoded size values because these structures contain one or more DNSQuestion
+    // instances.
+//    char sizecheck_NetworkInterfaceInfoOSX[(sizeof(NetworkInterfaceInfoOSX) <=  7084) ? 1 : -1];
+    char sizecheck_mDNS_PlatformSupport   [(sizeof(mDNS_PlatformSupport)    <=  1378) ? 1 : -1];
 };
 
 #ifdef  __cplusplus
 };
 
 #ifdef  __cplusplus
index 6c42a2e5a4a3a2fcff133b4093a696eca9417fe7..21b081088e9fbfd6e2d580562f8f122722403f2d 100644 (file)
@@ -4,5 +4,19 @@
 <dict>
     <key>com.apple.wifi.manager-access</key>
     <true/>
 <dict>
     <key>com.apple.wifi.manager-access</key>
     <true/>
+    <key>com.apple.SystemConfiguration.trailing-edge-agent</key>
+    <true/>
+    <key>com.apple.private.network.socket-delegate</key>
+    <true/>
+    <key>com.apple.networkd.cellular_blocked.notify</key>
+    <true/>
+    <key>com.apple.private.SCNetworkConnection-proxy-user</key>
+    <true/>
+    <key>com.apple.private.network.reserved-port</key>
+    <true/>
+    <key>com.apple.SystemConfiguration.SCDynamicStore-write-access</key>
+    <true/>
+    <key>com.apple.private.snhelper</key>
+    <true/>
 </dict>
 </plist>
 </dict>
 </plist>
index 7fa2643d7372c52febc7599b5b1d0ac0d5eb16ca..65b31ba8decf52cee7178fdd4ebd48af28b901fe 100644 (file)
@@ -53,6 +53,8 @@
        (global-name "com.apple.SecurityServer")
        (global-name "com.apple.SystemConfiguration.configd")
        (global-name "com.apple.SystemConfiguration.SCNetworkReachability")
        (global-name "com.apple.SecurityServer")
        (global-name "com.apple.SystemConfiguration.configd")
        (global-name "com.apple.SystemConfiguration.SCNetworkReachability")
+       (global-name "com.apple.SystemConfiguration.DNSConfiguration")
+       (global-name "com.apple.SystemConfiguration.NetworkInformation")
        (global-name "com.apple.system.notification_center")
        (global-name "com.apple.system.logger")
        (global-name "com.apple.webcontentfilter.dns")
        (global-name "com.apple.system.notification_center")
        (global-name "com.apple.system.logger")
        (global-name "com.apple.webcontentfilter.dns")
        (global-name "com.apple.networkd")
        (global-name "com.apple.securityd")
        (global-name "com.apple.wifi.manager")
        (global-name "com.apple.networkd")
        (global-name "com.apple.securityd")
        (global-name "com.apple.wifi.manager")
-       (global-name "com.apple.blued"))
+       (global-name "com.apple.commcenter.cupolicy.xpc")
+       (global-name "com.apple.blued")
+       (global-name "com.apple.snhelper"))
+
+(allow mach-register
+       (global-name "com.apple.d2d.ipc"))
 
 ; Networking, including Unix Domain Sockets
 (allow network*)
 
 ; Networking, including Unix Domain Sockets
 (allow network*)
        (literal "/Library/Preferences/SystemConfiguration/com.apple.PowerManagement.plist")
        (literal "/private/var/preferences/SystemConfiguration/preferences.plist"))
 
        (literal "/Library/Preferences/SystemConfiguration/com.apple.PowerManagement.plist")
        (literal "/private/var/preferences/SystemConfiguration/preferences.plist"))
 
+; For MAC Address
+(allow system-info (info-type "net.link.addr"))
+
 ; We just need access to System.keychain. But we don't want errors logged if other keychains are
 ; accessed under /Library/Keychains. Other keychains may be accessed as part of setting up an SSL
 ; connection. Instead of adding access to it here (to things which we don't need), we disable any
 ; We just need access to System.keychain. But we don't want errors logged if other keychains are
 ; accessed under /Library/Keychains. Other keychains may be accessed as part of setting up an SSL
 ; connection. Instead of adding access to it here (to things which we don't need), we disable any
 (deny file-read-data (regex #"^/Library/Keychains/") (with no-log))
 (allow file-read-data (literal "/Library/Keychains/System.keychain"))
 
 (deny file-read-data (regex #"^/Library/Keychains/") (with no-log))
 (allow file-read-data (literal "/Library/Keychains/System.keychain"))
 
+; Access to mDNSResponder Managed Preferences profile
+; instead of using (mobile-preferences-read "com.apple.mDNSResponder") we use the lines below for OSX compatibility
+(allow file-read* (literal "/private/var/Managed Preferences/mobile"))
+(allow file-read* (literal "/private/var/Library/Preferences/"))
+(allow file-read* (literal "/Library/Managed Preferences"))
+(allow file-read* (literal "/private/var/Managed Preferences/mobile/com.apple.mDNSResponder.plist"))
+
 ; Our Module Directory Services cache
 (allow file-read-data
        (subpath "/private/var/tmp/mds")
 ; Our Module Directory Services cache
 (allow file-read-data
        (subpath "/private/var/tmp/mds")
      (allow iokit-open
         (iokit-user-client-class "NVEthernetUserClientMDNS")
         (iokit-user-client-class "mDNSOffloadUserClient")
      (allow iokit-open
         (iokit-user-client-class "NVEthernetUserClientMDNS")
         (iokit-user-client-class "mDNSOffloadUserClient")
-        (iokit-user-client-class "RootDomainUserClient"))))
+        (iokit-user-client-class "wlDNSOffloadUserClient")
+        (iokit-user-client-class "RootDomainUserClient")
+        (iokit-user-client-class "AppleMobileFileIntegrityUserClient"))))
index 95f6f83225f472fd89b0ee352ddeed21a94b8f9d..597478f563fdcadac00d10bb65d5c78badbf326b 100644 (file)
@@ -33,6 +33,8 @@
                                03067D6A0C83A3890022BE1F /* PBXTargetDependency */,
                                03067D6C0C83A3920022BE1F /* PBXTargetDependency */,
                                03067D6E0C83A39C0022BE1F /* PBXTargetDependency */,
                                03067D6A0C83A3890022BE1F /* PBXTargetDependency */,
                                03067D6C0C83A3920022BE1F /* PBXTargetDependency */,
                                03067D6E0C83A39C0022BE1F /* PBXTargetDependency */,
+                               84C5B3411665544B00C324A8 /* PBXTargetDependency */,
+                               72FB546A166D5FE40090B2D9 /* PBXTargetDependency */,
                        );
                        name = "Build Some";
                        productName = "Build Some";
                        );
                        name = "Build Some";
                        productName = "Build Some";
 /* End PBXAggregateTarget section */
 
 /* Begin PBXBuildFile section */
 /* End PBXAggregateTarget section */
 
 /* Begin PBXBuildFile section */
+               21070E5F16486B9000A69507 /* DNSSECSupport.c in Sources */ = {isa = PBXBuildFile; fileRef = 21070E5D16486B9000A69507 /* DNSSECSupport.c */; };
+               21070E6016486B9000A69507 /* DNSSECSupport.c in Sources */ = {isa = PBXBuildFile; fileRef = 21070E5D16486B9000A69507 /* DNSSECSupport.c */; };
+               21070E6116486B9000A69507 /* DNSSECSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 21070E5E16486B9000A69507 /* DNSSECSupport.h */; };
+               21070E6216486B9000A69507 /* DNSSECSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 21070E5E16486B9000A69507 /* DNSSECSupport.h */; };
+               2120ABD516B71614007089B6 /* CUPolicy.c in Sources */ = {isa = PBXBuildFile; fileRef = 2120ABD416B71614007089B6 /* CUPolicy.c */; };
+               2120ABD616B71614007089B6 /* CUPolicy.c in Sources */ = {isa = PBXBuildFile; fileRef = 2120ABD416B71614007089B6 /* CUPolicy.c */; };
                2124FA2C1471E98C0021D7BB /* nsec.h in Headers */ = {isa = PBXBuildFile; fileRef = 2124FA2B1471E98C0021D7BB /* nsec.h */; };
                2124FA2D1471E98C0021D7BB /* nsec.h in Headers */ = {isa = PBXBuildFile; fileRef = 2124FA2B1471E98C0021D7BB /* nsec.h */; };
                2124FA301471E9B50021D7BB /* dnssec.h in Headers */ = {isa = PBXBuildFile; fileRef = 2124FA2F1471E9B50021D7BB /* dnssec.h */; };
                2124FA311471E9B50021D7BB /* dnssec.h in Headers */ = {isa = PBXBuildFile; fileRef = 2124FA2F1471E9B50021D7BB /* dnssec.h */; };
                2124FA331471E9DE0021D7BB /* nsec.c in Sources */ = {isa = PBXBuildFile; fileRef = 2124FA321471E9DE0021D7BB /* nsec.c */; };
                2124FA341471E9DE0021D7BB /* nsec.c in Sources */ = {isa = PBXBuildFile; fileRef = 2124FA321471E9DE0021D7BB /* nsec.c */; };
                2124FA2C1471E98C0021D7BB /* nsec.h in Headers */ = {isa = PBXBuildFile; fileRef = 2124FA2B1471E98C0021D7BB /* nsec.h */; };
                2124FA2D1471E98C0021D7BB /* nsec.h in Headers */ = {isa = PBXBuildFile; fileRef = 2124FA2B1471E98C0021D7BB /* nsec.h */; };
                2124FA301471E9B50021D7BB /* dnssec.h in Headers */ = {isa = PBXBuildFile; fileRef = 2124FA2F1471E9B50021D7BB /* dnssec.h */; };
                2124FA311471E9B50021D7BB /* dnssec.h in Headers */ = {isa = PBXBuildFile; fileRef = 2124FA2F1471E9B50021D7BB /* dnssec.h */; };
                2124FA331471E9DE0021D7BB /* nsec.c in Sources */ = {isa = PBXBuildFile; fileRef = 2124FA321471E9DE0021D7BB /* nsec.c */; };
                2124FA341471E9DE0021D7BB /* nsec.c in Sources */ = {isa = PBXBuildFile; fileRef = 2124FA321471E9DE0021D7BB /* nsec.c */; };
+               2127A47715C3C7B900A857FC /* nsec3.c in Sources */ = {isa = PBXBuildFile; fileRef = 2127A47515C3C7B900A857FC /* nsec3.c */; };
+               2127A47815C3C7B900A857FC /* nsec3.c in Sources */ = {isa = PBXBuildFile; fileRef = 2127A47515C3C7B900A857FC /* nsec3.c */; };
+               2127A47915C3C7B900A857FC /* nsec3.h in Headers */ = {isa = PBXBuildFile; fileRef = 2127A47615C3C7B900A857FC /* nsec3.h */; };
+               2127A47A15C3C7B900A857FC /* nsec3.h in Headers */ = {isa = PBXBuildFile; fileRef = 2127A47615C3C7B900A857FC /* nsec3.h */; };
                213BDC6D147319F400000896 /* dnssec.c in Sources */ = {isa = PBXBuildFile; fileRef = 213BDC6C147319F400000896 /* dnssec.c */; };
                213BDC6E147319F400000896 /* dnssec.c in Sources */ = {isa = PBXBuildFile; fileRef = 213BDC6C147319F400000896 /* dnssec.c */; };
                213FB23C12028C4A002B3A08 /* BonjourEvents.c in Sources */ = {isa = PBXBuildFile; fileRef = 213FB22C12028B53002B3A08 /* BonjourEvents.c */; };
                213BDC6D147319F400000896 /* dnssec.c in Sources */ = {isa = PBXBuildFile; fileRef = 213BDC6C147319F400000896 /* dnssec.c */; };
                213BDC6E147319F400000896 /* dnssec.c in Sources */ = {isa = PBXBuildFile; fileRef = 213BDC6C147319F400000896 /* dnssec.c */; };
                213FB23C12028C4A002B3A08 /* BonjourEvents.c in Sources */ = {isa = PBXBuildFile; fileRef = 213FB22C12028B53002B3A08 /* BonjourEvents.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, ); }; };
                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, ); }; };
+               216D9ACE1720C9F5008066E1 /* VPNService.c in Sources */ = {isa = PBXBuildFile; fileRef = 216D9ACD1720C9F5008066E1 /* VPNService.c */; };
+               216D9ACF1720C9F5008066E1 /* VPNService.c in Sources */ = {isa = PBXBuildFile; fileRef = 216D9ACD1720C9F5008066E1 /* VPNService.c */; };
+               218E8E51156D8C0300720DA0 /* dnsproxy.c in Sources */ = {isa = PBXBuildFile; fileRef = 218E8E4F156D8C0300720DA0 /* dnsproxy.c */; };
+               218E8E52156D8C0300720DA0 /* dnsproxy.c in Sources */ = {isa = PBXBuildFile; fileRef = 218E8E4F156D8C0300720DA0 /* dnsproxy.c */; };
+               218E8E53156D8C0300720DA0 /* dnsproxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 218E8E50156D8C0300720DA0 /* dnsproxy.h */; };
+               218E8E54156D8C0300720DA0 /* dnsproxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 218E8E50156D8C0300720DA0 /* dnsproxy.h */; };
                219D5542149ED645004464AE /* libxml2.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 219D5541149ED645004464AE /* libxml2.2.dylib */; };
                219D5543149ED645004464AE /* libxml2.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 219D5541149ED645004464AE /* libxml2.2.dylib */; };
                21A57F4C145B2AE100939099 /* CryptoAlg.c in Sources */ = {isa = PBXBuildFile; fileRef = 21A57F4A145B2AE100939099 /* CryptoAlg.c */; };
                219D5542149ED645004464AE /* libxml2.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 219D5541149ED645004464AE /* libxml2.2.dylib */; };
                219D5543149ED645004464AE /* libxml2.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 219D5541149ED645004464AE /* libxml2.2.dylib */; };
                21A57F4C145B2AE100939099 /* CryptoAlg.c in Sources */ = {isa = PBXBuildFile; fileRef = 21A57F4A145B2AE100939099 /* CryptoAlg.c */; };
                21A57F56145B2B1400939099 /* CryptoSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 21A57F52145B2B1400939099 /* CryptoSupport.h */; };
                21DCD05C1461B23700702FC8 /* CryptoAlg.c in Sources */ = {isa = PBXBuildFile; fileRef = 21A57F4A145B2AE100939099 /* CryptoAlg.c */; };
                21DCD05D1461B23700702FC8 /* CryptoAlg.h in Sources */ = {isa = PBXBuildFile; fileRef = 21A57F4B145B2AE100939099 /* CryptoAlg.h */; };
                21A57F56145B2B1400939099 /* CryptoSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 21A57F52145B2B1400939099 /* CryptoSupport.h */; };
                21DCD05C1461B23700702FC8 /* CryptoAlg.c in Sources */ = {isa = PBXBuildFile; fileRef = 21A57F4A145B2AE100939099 /* CryptoAlg.c */; };
                21DCD05D1461B23700702FC8 /* CryptoAlg.h in Sources */ = {isa = PBXBuildFile; fileRef = 21A57F4B145B2AE100939099 /* CryptoAlg.h */; };
+               21DD8FBF161E9A250033C8F8 /* anonymous.c in Sources */ = {isa = PBXBuildFile; fileRef = 21DD8FBD161E9A250033C8F8 /* anonymous.c */; };
+               21DD8FC0161E9A250033C8F8 /* anonymous.c in Sources */ = {isa = PBXBuildFile; fileRef = 21DD8FBD161E9A250033C8F8 /* anonymous.c */; };
+               21DD8FC1161E9A250033C8F8 /* anonymous.h in Headers */ = {isa = PBXBuildFile; fileRef = 21DD8FBE161E9A250033C8F8 /* anonymous.h */; };
+               21DD8FC2161E9A250033C8F8 /* anonymous.h in Headers */ = {isa = PBXBuildFile; fileRef = 21DD8FBE161E9A250033C8F8 /* anonymous.h */; };
+               21DED43515702C0F0060B6B9 /* DNSProxySupport.c in Sources */ = {isa = PBXBuildFile; fileRef = 21DED43415702C0F0060B6B9 /* DNSProxySupport.c */; };
+               21DED43615702C0F0060B6B9 /* DNSProxySupport.c in Sources */ = {isa = PBXBuildFile; fileRef = 21DED43415702C0F0060B6B9 /* DNSProxySupport.c */; };
                2E0405F50C3195F700F13B59 /* helper.c in Sources */ = {isa = PBXBuildFile; fileRef = 2E0405F40C3195F700F13B59 /* helper.c */; };
                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"; }; };
+               2E0405F60C31961100F13B59 /* helpermsg.defs in Sources */ = {isa = PBXBuildFile; fileRef = 2E0405EB0C3190DC00F13B59 /* helpermsg.defs */; settings = {ATTRIBUTES = (Client, Server, ); COMPILER_FLAGS = "-Wno-error"; }; };
                2E0406150C3197CB00F13B59 /* libbsm.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 2E0406140C3197CB00F13B59 /* libbsm.dylib */; };
                2E04061F0C3198B700F13B59 /* helpermsg.defs in Sources */ = {isa = PBXBuildFile; fileRef = 2E0405EB0C3190DC00F13B59 /* helpermsg.defs */; };
                2E0406200C3198B700F13B59 /* helpermsg.defs in Sources */ = {isa = PBXBuildFile; fileRef = 2E0405EB0C3190DC00F13B59 /* helpermsg.defs */; };
                2E0406150C3197CB00F13B59 /* libbsm.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 2E0406140C3197CB00F13B59 /* libbsm.dylib */; };
                2E04061F0C3198B700F13B59 /* helpermsg.defs in Sources */ = {isa = PBXBuildFile; fileRef = 2E0405EB0C3190DC00F13B59 /* helpermsg.defs */; };
                2E0406200C3198B700F13B59 /* helpermsg.defs in Sources */ = {isa = PBXBuildFile; fileRef = 2E0405EB0C3190DC00F13B59 /* helpermsg.defs */; };
                4AAE0C9A0C68EA81003882A5 /* mDNSResponderHelper.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4AAE0C7A0C68E97F003882A5 /* mDNSResponderHelper.8 */; };
                4BD2B63A134FE09F002B96D5 /* P2PPacketFilter.c in Sources */ = {isa = PBXBuildFile; fileRef = 4BD2B638134FE09F002B96D5 /* P2PPacketFilter.c */; };
                4BD2B63B134FE09F002B96D5 /* P2PPacketFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BD2B639134FE09F002B96D5 /* P2PPacketFilter.h */; };
                4AAE0C9A0C68EA81003882A5 /* mDNSResponderHelper.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4AAE0C7A0C68E97F003882A5 /* mDNSResponderHelper.8 */; };
                4BD2B63A134FE09F002B96D5 /* P2PPacketFilter.c in Sources */ = {isa = PBXBuildFile; fileRef = 4BD2B638134FE09F002B96D5 /* P2PPacketFilter.c */; };
                4BD2B63B134FE09F002B96D5 /* P2PPacketFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BD2B639134FE09F002B96D5 /* P2PPacketFilter.h */; };
+               72FB5467166D5FCA0090B2D9 /* dnsctl.c in Sources */ = {isa = PBXBuildFile; fileRef = 72FB545A166D5F960090B2D9 /* dnsctl.c */; };
+               72FB5468166D5FD20090B2D9 /* libdns_services.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 84C5B3351665529800C324A8 /* libdns_services.dylib */; };
+               8418673E15AB8C2D00BB7F70 /* com.apple.networking.mDNSResponder in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8418673A15AB8B6900BB7F70 /* com.apple.networking.mDNSResponder */; };
+               848DA5C7165477E000D2E8B4 /* xpc_services.c in Sources */ = {isa = PBXBuildFile; fileRef = 848DA5C6165477E000D2E8B4 /* xpc_services.c */; };
+               848DA5C8165477E000D2E8B4 /* xpc_services.c in Sources */ = {isa = PBXBuildFile; fileRef = 848DA5C6165477E000D2E8B4 /* xpc_services.c */; };
+               848DA5CA165477EB00D2E8B4 /* xpc_services.h in Headers */ = {isa = PBXBuildFile; fileRef = 848DA5C9165477EB00D2E8B4 /* xpc_services.h */; };
+               848DA5CB165477EB00D2E8B4 /* xpc_services.h in Headers */ = {isa = PBXBuildFile; fileRef = 848DA5C9165477EB00D2E8B4 /* xpc_services.h */; };
+               848DA5D616547F7200D2E8B4 /* dns_xpc.h in Headers */ = {isa = PBXBuildFile; fileRef = 848DA5D516547F7200D2E8B4 /* dns_xpc.h */; };
+               848DA5D716547F7200D2E8B4 /* dns_xpc.h in Headers */ = {isa = PBXBuildFile; fileRef = 848DA5D516547F7200D2E8B4 /* dns_xpc.h */; };
+               84C5B33C166553F100C324A8 /* dns_services.c in Sources */ = {isa = PBXBuildFile; fileRef = 84C5B339166553AF00C324A8 /* dns_services.c */; };
+               84C5B33D166553F900C324A8 /* dns_services.h in Headers */ = {isa = PBXBuildFile; fileRef = 84C5B338166553A000C324A8 /* dns_services.h */; settings = {ATTRIBUTES = (Private, ); }; };
                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, ); }; };
                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, ); }; };
                        remoteGlobalIDString = 4AE471670EAFF81900A6C5AD;
                        remoteInfo = dns_sd.jar;
                };
                        remoteGlobalIDString = 4AE471670EAFF81900A6C5AD;
                        remoteInfo = dns_sd.jar;
                };
+               72FB5469166D5FE40090B2D9 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 72FB545E166D5FB00090B2D9;
+                       remoteInfo = dnsctl;
+               };
+               84C5B3401665544B00C324A8 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 84C5B3341665529800C324A8;
+                       remoteInfo = dns_services;
+               };
                D284BF2B0ADD815A0027CCDF /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
                D284BF2B0ADD815A0027CCDF /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                };
+               72FB545D166D5FB00090B2D9 /* CopyFiles */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 12;
+                       dstPath = /usr/share/man/man1/;
+                       dstSubfolderSpec = 0;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               8418673D15AB8BFF00BB7F70 /* CopyFiles */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 8;
+                       dstPath = /private/etc/asl/;
+                       dstSubfolderSpec = 0;
+                       files = (
+                               8418673E15AB8C2D00BB7F70 /* com.apple.networking.mDNSResponder in CopyFiles */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 1;
+               };
                D284BE6A0ADD80740027CCDF /* CopyFiles */ = {
                        isa = PBXCopyFilesBuildPhase;
                        buildActionMask = 8;
                D284BE6A0ADD80740027CCDF /* 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>"; };
                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>"; };
+               21070E5D16486B9000A69507 /* DNSSECSupport.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = DNSSECSupport.c; sourceTree = "<group>"; };
+               21070E5E16486B9000A69507 /* DNSSECSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNSSECSupport.h; sourceTree = "<group>"; };
+               2120ABD416B71614007089B6 /* CUPolicy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = CUPolicy.c; sourceTree = "<group>"; };
                2124FA2B1471E98C0021D7BB /* nsec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nsec.h; path = ../mDNSCore/nsec.h; sourceTree = "<group>"; };
                2124FA2F1471E9B50021D7BB /* dnssec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dnssec.h; path = ../mDNSCore/dnssec.h; sourceTree = "<group>"; };
                2124FA321471E9DE0021D7BB /* nsec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nsec.c; path = ../mDNSCore/nsec.c; sourceTree = "<group>"; };
                2124FA2B1471E98C0021D7BB /* nsec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nsec.h; path = ../mDNSCore/nsec.h; sourceTree = "<group>"; };
                2124FA2F1471E9B50021D7BB /* dnssec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dnssec.h; path = ../mDNSCore/dnssec.h; sourceTree = "<group>"; };
                2124FA321471E9DE0021D7BB /* nsec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nsec.c; path = ../mDNSCore/nsec.c; sourceTree = "<group>"; };
+               2127A47515C3C7B900A857FC /* nsec3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nsec3.c; path = ../mDNSCore/nsec3.c; sourceTree = "<group>"; };
+               2127A47615C3C7B900A857FC /* nsec3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nsec3.h; path = ../mDNSCore/nsec3.h; sourceTree = "<group>"; };
                213BDC6C147319F400000896 /* dnssec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dnssec.c; path = ../mDNSCore/dnssec.c; sourceTree = "<group>"; };
                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>"; };
                213BDC6C147319F400000896 /* dnssec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dnssec.c; path = ../mDNSCore/dnssec.c; sourceTree = "<group>"; };
                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>"; };
                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; };
                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; };
+               216D9ACD1720C9F5008066E1 /* VPNService.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = VPNService.c; sourceTree = "<group>"; };
+               218E8E4F156D8C0300720DA0 /* dnsproxy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dnsproxy.c; path = ../mDNSCore/dnsproxy.c; sourceTree = "<group>"; };
+               218E8E50156D8C0300720DA0 /* dnsproxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dnsproxy.h; path = ../mDNSCore/dnsproxy.h; sourceTree = "<group>"; };
                219D5541149ED645004464AE /* libxml2.2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libxml2.2.dylib; path = SDKs/MacOSX10.8.sdk/usr/lib/libxml2.2.dylib; sourceTree = DEVELOPER_DIR; };
                21A57F4A145B2AE100939099 /* CryptoAlg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = CryptoAlg.c; path = ../mDNSCore/CryptoAlg.c; sourceTree = "<group>"; };
                21A57F4B145B2AE100939099 /* CryptoAlg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CryptoAlg.h; path = ../mDNSCore/CryptoAlg.h; sourceTree = "<group>"; };
                21A57F51145B2B1400939099 /* CryptoSupport.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = CryptoSupport.c; sourceTree = "<group>"; };
                21A57F52145B2B1400939099 /* CryptoSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CryptoSupport.h; sourceTree = "<group>"; };
                219D5541149ED645004464AE /* libxml2.2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libxml2.2.dylib; path = SDKs/MacOSX10.8.sdk/usr/lib/libxml2.2.dylib; sourceTree = DEVELOPER_DIR; };
                21A57F4A145B2AE100939099 /* CryptoAlg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = CryptoAlg.c; path = ../mDNSCore/CryptoAlg.c; sourceTree = "<group>"; };
                21A57F4B145B2AE100939099 /* CryptoAlg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CryptoAlg.h; path = ../mDNSCore/CryptoAlg.h; sourceTree = "<group>"; };
                21A57F51145B2B1400939099 /* CryptoSupport.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = CryptoSupport.c; sourceTree = "<group>"; };
                21A57F52145B2B1400939099 /* CryptoSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CryptoSupport.h; sourceTree = "<group>"; };
+               21DD8FBD161E9A250033C8F8 /* anonymous.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = anonymous.c; path = ../mDNSCore/anonymous.c; sourceTree = "<group>"; };
+               21DD8FBE161E9A250033C8F8 /* anonymous.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = anonymous.h; path = ../mDNSCore/anonymous.h; sourceTree = "<group>"; };
+               21DED43415702C0F0060B6B9 /* DNSProxySupport.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = DNSProxySupport.c; sourceTree = "<group>"; };
                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; };
                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; };
                6575FC00022EAFBA00000109 /* DNSServiceDiscoveryReply.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; path = DNSServiceDiscoveryReply.defs; sourceTree = "<group>"; };
                6575FC01022EAFBA00000109 /* DNSServiceDiscoveryRequest.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; path = DNSServiceDiscoveryRequest.defs; sourceTree = "<group>"; };
                6575FC20022EB7AA00000109 /* SamplemDNSClient.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; path = SamplemDNSClient.c; sourceTree = SOURCE_ROOT; tabWidth = 4; usesTabs = 0; };
                6575FC00022EAFBA00000109 /* DNSServiceDiscoveryReply.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; path = DNSServiceDiscoveryReply.defs; sourceTree = "<group>"; };
                6575FC01022EAFBA00000109 /* DNSServiceDiscoveryRequest.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; path = DNSServiceDiscoveryRequest.defs; sourceTree = "<group>"; };
                6575FC20022EB7AA00000109 /* SamplemDNSClient.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; path = SamplemDNSClient.c; sourceTree = SOURCE_ROOT; tabWidth = 4; usesTabs = 0; };
+               72FB545A166D5F960090B2D9 /* dnsctl.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = dnsctl.c; path = ../Clients/dnsctl.c; sourceTree = "<group>"; };
+               72FB545F166D5FB00090B2D9 /* dnsctl */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dnsctl; sourceTree = BUILT_PRODUCTS_DIR; };
                7F18A9F60587CEF6001880B3 /* DNSCommon.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = DNSCommon.c; path = ../mDNSCore/DNSCommon.c; sourceTree = SOURCE_ROOT; };
                7F18A9F70587CEF6001880B3 /* uDNS.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = uDNS.c; path = ../mDNSCore/uDNS.c; sourceTree = SOURCE_ROOT; };
                7F461DB5062DBF2900672BF3 /* DNSDigest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = DNSDigest.c; path = ../mDNSCore/DNSDigest.c; sourceTree = SOURCE_ROOT; };
                7F869685066EE02400D2A2DC /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = /System/Library/Frameworks/Security.framework; sourceTree = "<absolute>"; };
                7FC8F9D406D14E66007E879D /* LegacyNATTraversal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = LegacyNATTraversal.c; sourceTree = SOURCE_ROOT; };
                7F18A9F60587CEF6001880B3 /* DNSCommon.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = DNSCommon.c; path = ../mDNSCore/DNSCommon.c; sourceTree = SOURCE_ROOT; };
                7F18A9F70587CEF6001880B3 /* uDNS.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = uDNS.c; path = ../mDNSCore/uDNS.c; sourceTree = SOURCE_ROOT; };
                7F461DB5062DBF2900672BF3 /* DNSDigest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = DNSDigest.c; path = ../mDNSCore/DNSDigest.c; sourceTree = SOURCE_ROOT; };
                7F869685066EE02400D2A2DC /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = /System/Library/Frameworks/Security.framework; sourceTree = "<absolute>"; };
                7FC8F9D406D14E66007E879D /* LegacyNATTraversal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = LegacyNATTraversal.c; sourceTree = SOURCE_ROOT; };
+               8418673A15AB8B6900BB7F70 /* com.apple.networking.mDNSResponder */ = {isa = PBXFileReference; lastKnownFileType = text; path = com.apple.networking.mDNSResponder; sourceTree = "<group>"; };
+               8418673C15AB8B8000BB7F70 /* mDNSResponderLogging.mobileconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = mDNSResponderLogging.mobileconfig; sourceTree = "<group>"; };
+               848DA5C6165477E000D2E8B4 /* xpc_services.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = xpc_services.c; path = Private/xpc_services.c; sourceTree = "<group>"; };
+               848DA5C9165477EB00D2E8B4 /* xpc_services.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = xpc_services.h; path = Private/xpc_services.h; sourceTree = "<group>"; };
+               848DA5D516547F7200D2E8B4 /* dns_xpc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dns_xpc.h; path = Private/dns_xpc.h; sourceTree = "<group>"; };
+               84C5B3351665529800C324A8 /* libdns_services.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libdns_services.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
+               84C5B338166553A000C324A8 /* dns_services.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = dns_services.h; path = Private/dns_services.h; sourceTree = "<group>"; };
+               84C5B339166553AF00C324A8 /* dns_services.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = dns_services.c; path = Private/dns_services.c; sourceTree = "<group>"; };
                D284BE730ADD80740027CCDF /* mDNSResponder */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mDNSResponder; sourceTree = BUILT_PRODUCTS_DIR; };
                D284BE950ADD80800027CCDF /* mDNSResponder.debug */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mDNSResponder.debug; sourceTree = BUILT_PRODUCTS_DIR; };
                D284BEB00ADD80920027CCDF /* dns-sd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "dns-sd"; sourceTree = BUILT_PRODUCTS_DIR; };
                D284BE730ADD80740027CCDF /* mDNSResponder */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mDNSResponder; sourceTree = BUILT_PRODUCTS_DIR; };
                D284BE950ADD80800027CCDF /* mDNSResponder.debug */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mDNSResponder.debug; sourceTree = BUILT_PRODUCTS_DIR; };
                D284BEB00ADD80920027CCDF /* dns-sd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "dns-sd"; sourceTree = BUILT_PRODUCTS_DIR; };
                F5E11B5A04A28126019798ED /* dnssd_ipc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dnssd_ipc.c; path = ../mDNSShared/dnssd_ipc.c; sourceTree = SOURCE_ROOT; };
                F5E11B5B04A28126019798ED /* dnssd_ipc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dnssd_ipc.h; path = ../mDNSShared/dnssd_ipc.h; sourceTree = SOURCE_ROOT; };
                FF08480607CEB8E800AE6769 /* inprogress.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = inprogress.tiff; path = PreferencePane/Artwork/inprogress.tiff; sourceTree = SOURCE_ROOT; };
                F5E11B5A04A28126019798ED /* dnssd_ipc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dnssd_ipc.c; path = ../mDNSShared/dnssd_ipc.c; sourceTree = SOURCE_ROOT; };
                F5E11B5B04A28126019798ED /* dnssd_ipc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dnssd_ipc.h; path = ../mDNSShared/dnssd_ipc.h; sourceTree = SOURCE_ROOT; };
                FF08480607CEB8E800AE6769 /* inprogress.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = inprogress.tiff; path = PreferencePane/Artwork/inprogress.tiff; sourceTree = SOURCE_ROOT; };
-               FF0E0B5D065ADC7600FE4D9C /* mDNS.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = mDNS.1; path = ../mDNSShared/mDNS.1; sourceTree = SOURCE_ROOT; };
                FF13FFEA0A5DA44A00897C81 /* dnsextd_lexer.l */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.lex; name = dnsextd_lexer.l; path = ../mDNSShared/dnsextd_lexer.l; sourceTree = SOURCE_ROOT; };
                FF13FFEC0A5DA45500897C81 /* dnsextd_parser.y */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.yacc; name = dnsextd_parser.y; path = ../mDNSShared/dnsextd_parser.y; sourceTree = SOURCE_ROOT; };
                FF1C919D07021D77001048AB /* dns-sd.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = "dns-sd.1"; path = "../mDNSShared/dns-sd.1"; sourceTree = SOURCE_ROOT; };
                FF13FFEA0A5DA44A00897C81 /* dnsextd_lexer.l */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.lex; name = dnsextd_lexer.l; path = ../mDNSShared/dnsextd_lexer.l; sourceTree = SOURCE_ROOT; };
                FF13FFEC0A5DA45500897C81 /* dnsextd_parser.y */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.yacc; name = dnsextd_parser.y; path = ../mDNSShared/dnsextd_parser.y; sourceTree = SOURCE_ROOT; };
                FF1C919D07021D77001048AB /* dns-sd.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = "dns-sd.1"; path = "../mDNSShared/dns-sd.1"; sourceTree = SOURCE_ROOT; };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
+               72FB545C166D5FB00090B2D9 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               72FB5468166D5FD20090B2D9 /* libdns_services.dylib in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               84C5B3321665529800C324A8 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                D284BE640ADD80740027CCDF /* Frameworks */ = {
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                D284BE640ADD80740027CCDF /* Frameworks */ = {
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                08FB7795FE84155DC02AAC07 /* mDNS Server Sources */ = {
                        isa = PBXGroup;
                        children = (
                08FB7795FE84155DC02AAC07 /* mDNS Server Sources */ = {
                        isa = PBXGroup;
                        children = (
+                               216D9ACD1720C9F5008066E1 /* VPNService.c */,
+                               2120ABD416B71614007089B6 /* CUPolicy.c */,
+                               84C5B338166553A000C324A8 /* dns_services.h */,
+                               72FB545A166D5F960090B2D9 /* dnsctl.c */,
+                               84C5B339166553AF00C324A8 /* dns_services.c */,
+                               848DA5D516547F7200D2E8B4 /* dns_xpc.h */,
+                               848DA5C9165477EB00D2E8B4 /* xpc_services.h */,
+                               848DA5C6165477E000D2E8B4 /* xpc_services.c */,
+                               21070E5D16486B9000A69507 /* DNSSECSupport.c */,
+                               21070E5E16486B9000A69507 /* DNSSECSupport.h */,
+                               21DD8FBD161E9A250033C8F8 /* anonymous.c */,
+                               21DD8FBE161E9A250033C8F8 /* anonymous.h */,
+                               2127A47515C3C7B900A857FC /* nsec3.c */,
+                               2127A47615C3C7B900A857FC /* nsec3.h */,
+                               8418673C15AB8B8000BB7F70 /* mDNSResponderLogging.mobileconfig */,
+                               8418673A15AB8B6900BB7F70 /* com.apple.networking.mDNSResponder */,
+                               21DED43415702C0F0060B6B9 /* DNSProxySupport.c */,
+                               218E8E4F156D8C0300720DA0 /* dnsproxy.c */,
+                               218E8E50156D8C0300720DA0 /* dnsproxy.h */,
                                213BDC6C147319F400000896 /* dnssec.c */,
                                2124FA321471E9DE0021D7BB /* nsec.c */,
                                2124FA2F1471E9B50021D7BB /* dnssec.h */,
                                213BDC6C147319F400000896 /* dnssec.c */,
                                2124FA321471E9DE0021D7BB /* nsec.c */,
                                2124FA2F1471E9B50021D7BB /* dnssec.h */,
                                000753D303367C1C0CCA2C71 /* mDNSMacOSX.h */,
                                DBAAFE2C057E8F660085CAD0 /* GenLinkedList.c */,
                                FFCB6D73075D539900B8AF62 /* PlatformCommon.c */,
                                000753D303367C1C0CCA2C71 /* mDNSMacOSX.h */,
                                DBAAFE2C057E8F660085CAD0 /* GenLinkedList.c */,
                                FFCB6D73075D539900B8AF62 /* PlatformCommon.c */,
-                               FF0E0B5D065ADC7600FE4D9C /* mDNS.1 */,
                                FF1C919D07021D77001048AB /* dns-sd.1 */,
                                FF485D5105632E0000130380 /* mDNSResponder.8 */,
                                FFF4F63A06CFE4DD00459EFD /* dnsextd.8 */,
                                FF1C919D07021D77001048AB /* dns-sd.1 */,
                                FF485D5105632E0000130380 /* mDNSResponder.8 */,
                                FFF4F63A06CFE4DD00459EFD /* dnsextd.8 */,
                                2141DD1D123FFCDB0086D23E /* libdns_sd.a */,
                                2141DD24123FFD0F0086D23E /* libdns_sd_debug.a */,
                                2141DD2A123FFD2C0086D23E /* libdns_sd_profile.a */,
                                2141DD1D123FFCDB0086D23E /* libdns_sd.a */,
                                2141DD24123FFD0F0086D23E /* libdns_sd_debug.a */,
                                2141DD2A123FFD2C0086D23E /* libdns_sd_profile.a */,
+                               84C5B3351665529800C324A8 /* libdns_services.dylib */,
+                               72FB545F166D5FB00090B2D9 /* dnsctl */,
                        );
                        name = Products;
                        sourceTree = "<group>";
                        );
                        name = Products;
                        sourceTree = "<group>";
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
+               84C5B3331665529800C324A8 /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               84C5B33D166553F900C324A8 /* dns_services.h in Headers */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                D284BE520ADD80740027CCDF /* Headers */ = {
                        isa = PBXHeadersBuildPhase;
                        buildActionMask = 2147483647;
                D284BE520ADD80740027CCDF /* Headers */ = {
                        isa = PBXHeadersBuildPhase;
                        buildActionMask = 2147483647;
                                21A57F55145B2B1400939099 /* CryptoSupport.h in Headers */,
                                2124FA2C1471E98C0021D7BB /* nsec.h in Headers */,
                                2124FA301471E9B50021D7BB /* dnssec.h in Headers */,
                                21A57F55145B2B1400939099 /* CryptoSupport.h in Headers */,
                                2124FA2C1471E98C0021D7BB /* nsec.h in Headers */,
                                2124FA301471E9B50021D7BB /* dnssec.h in Headers */,
+                               218E8E53156D8C0300720DA0 /* dnsproxy.h in Headers */,
+                               2127A47915C3C7B900A857FC /* nsec3.h in Headers */,
+                               21DD8FC1161E9A250033C8F8 /* anonymous.h in Headers */,
+                               21070E6116486B9000A69507 /* DNSSECSupport.h in Headers */,
+                               848DA5CA165477EB00D2E8B4 /* xpc_services.h in Headers */,
+                               848DA5D616547F7200D2E8B4 /* dns_xpc.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                21A57F56145B2B1400939099 /* CryptoSupport.h in Headers */,
                                2124FA2D1471E98C0021D7BB /* nsec.h in Headers */,
                                2124FA311471E9B50021D7BB /* dnssec.h in Headers */,
                                21A57F56145B2B1400939099 /* CryptoSupport.h in Headers */,
                                2124FA2D1471E98C0021D7BB /* nsec.h in Headers */,
                                2124FA311471E9B50021D7BB /* dnssec.h in Headers */,
+                               218E8E54156D8C0300720DA0 /* dnsproxy.h in Headers */,
+                               2127A47A15C3C7B900A857FC /* nsec3.h in Headers */,
+                               21DD8FC2161E9A250033C8F8 /* anonymous.h in Headers */,
+                               21070E6216486B9000A69507 /* DNSSECSupport.h in Headers */,
+                               848DA5CB165477EB00D2E8B4 /* xpc_services.h in Headers */,
+                               848DA5D716547F7200D2E8B4 /* dns_xpc.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        productReference = 2E0405F00C31955500F13B59 /* mDNSResponderHelper */;
                        productType = "com.apple.product-type.tool";
                };
                        productReference = 2E0405F00C31955500F13B59 /* mDNSResponderHelper */;
                        productType = "com.apple.product-type.tool";
                };
+               72FB545E166D5FB00090B2D9 /* dnsctl */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 72FB5465166D5FB00090B2D9 /* Build configuration list for PBXNativeTarget "dnsctl" */;
+                       buildPhases = (
+                               72FB545B166D5FB00090B2D9 /* Sources */,
+                               72FB545C166D5FB00090B2D9 /* Frameworks */,
+                               72FB545D166D5FB00090B2D9 /* CopyFiles */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = dnsctl;
+                       productName = dnsctl;
+                       productReference = 72FB545F166D5FB00090B2D9 /* dnsctl */;
+                       productType = "com.apple.product-type.tool";
+               };
+               84C5B3341665529800C324A8 /* dns_services */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 84C5B3361665529800C324A8 /* Build configuration list for PBXNativeTarget "dns_services" */;
+                       buildPhases = (
+                               84C5B3311665529800C324A8 /* Sources */,
+                               84C5B3321665529800C324A8 /* Frameworks */,
+                               84C5B3331665529800C324A8 /* Headers */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = dns_services;
+                       productName = dns_services;
+                       productReference = 84C5B3351665529800C324A8 /* libdns_services.dylib */;
+                       productType = "com.apple.product-type.library.dynamic";
+               };
                D284BE500ADD80740027CCDF /* mDNSResponder */ = {
                        isa = PBXNativeTarget;
                        buildConfigurationList = D284BE6D0ADD80740027CCDF /* Build configuration list for PBXNativeTarget "mDNSResponder" */;
                D284BE500ADD80740027CCDF /* mDNSResponder */ = {
                        isa = PBXNativeTarget;
                        buildConfigurationList = D284BE6D0ADD80740027CCDF /* Build configuration list for PBXNativeTarget "mDNSResponder" */;
                                4A7B9E7F14FDA21B00B84CC1 /* CopyFiles */,
                                4A7B9E8114FDA25500B84CC1 /* CopyFiles */,
                                D284BE6C0ADD80740027CCDF /* ShellScript */,
                                4A7B9E7F14FDA21B00B84CC1 /* CopyFiles */,
                                4A7B9E8114FDA25500B84CC1 /* CopyFiles */,
                                D284BE6C0ADD80740027CCDF /* ShellScript */,
+                               8418673D15AB8BFF00BB7F70 /* CopyFiles */,
                        );
                        buildRules = (
                        );
                        );
                        buildRules = (
                        );
 /* Begin PBXProject section */
                08FB7793FE84155DC02AAC07 /* Project object */ = {
                        isa = PBXProject;
 /* Begin PBXProject section */
                08FB7793FE84155DC02AAC07 /* Project object */ = {
                        isa = PBXProject;
+                       attributes = {
+                       };
                        buildConfigurationList = D284BE2B0ADD78180027CCDF /* Build configuration list for PBXProject "mDNSResponder" */;
                        compatibilityVersion = "Xcode 3.1";
                        developmentRegion = English;
                        buildConfigurationList = D284BE2B0ADD78180027CCDF /* Build configuration list for PBXProject "mDNSResponder" */;
                        compatibilityVersion = "Xcode 3.1";
                        developmentRegion = English;
                                2141DD1C123FFCDB0086D23E /* libdns_sd_static */,
                                2141DD23123FFD0F0086D23E /* libdns_sd_debug_static */,
                                2141DD29123FFD2C0086D23E /* libdns_sd_profile_static */,
                                2141DD1C123FFCDB0086D23E /* libdns_sd_static */,
                                2141DD23123FFD0F0086D23E /* libdns_sd_debug_static */,
                                2141DD29123FFD2C0086D23E /* libdns_sd_profile_static */,
+                               84C5B3341665529800C324A8 /* dns_services */,
+                               72FB545E166D5FB00090B2D9 /* dnsctl */,
                        );
                };
 /* End PBXProject section */
                        );
                };
 /* End PBXProject section */
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/sh;
                        );
                        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\"";
+                       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\"\nsed 's/\\(^#define _DNS_SD_LIBDISPATCH \\)0$/\\1 1/' \"$SRCROOT/../mDNSShared/dns_sd.h\" > \"$DSTROOT/usr/include/dns_sd.h\"";
                };
                21DE714D115831CB00DD4BD1 /* ShellScript */ = {
                        isa = PBXShellScriptBuildPhase;
                };
                21DE714D115831CB00DD4BD1 /* ShellScript */ = {
                        isa = PBXShellScriptBuildPhase;
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/sh;
                        );
                        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\"";
+                       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\"\nsed 's/\\(^#define _DNS_SD_LIBDISPATCH \\)0$/\\1 1/' \"$SRCROOT/../mDNSShared/dns_sd.h\" > \"$DSTROOT/usr/include/dns_sd.h\"";
                };
                4A4EE3A413CB8E82005C624B /* Build yacc file into derived source files */ = {
                        isa = PBXShellScriptBuildPhase;
                };
                4A4EE3A413CB8E82005C624B /* Build yacc file into derived source files */ = {
                        isa = PBXShellScriptBuildPhase;
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
+               72FB545B166D5FB00090B2D9 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               72FB5467166D5FCA0090B2D9 /* dnsctl.c in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               84C5B3311665529800C324A8 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               84C5B33C166553F100C324A8 /* dns_services.c in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                D284BE550ADD80740027CCDF /* Sources */ = {
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                D284BE550ADD80740027CCDF /* Sources */ = {
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                                21A57F53145B2B1400939099 /* CryptoSupport.c in Sources */,
                                2124FA331471E9DE0021D7BB /* nsec.c in Sources */,
                                213BDC6D147319F400000896 /* dnssec.c in Sources */,
                                21A57F53145B2B1400939099 /* CryptoSupport.c in Sources */,
                                2124FA331471E9DE0021D7BB /* nsec.c in Sources */,
                                213BDC6D147319F400000896 /* dnssec.c in Sources */,
+                               218E8E51156D8C0300720DA0 /* dnsproxy.c in Sources */,
+                               21DED43515702C0F0060B6B9 /* DNSProxySupport.c in Sources */,
+                               216D9ACE1720C9F5008066E1 /* VPNService.c in Sources */,
+                               2127A47715C3C7B900A857FC /* nsec3.c in Sources */,
+                               21DD8FBF161E9A250033C8F8 /* anonymous.c in Sources */,
+                               21070E5F16486B9000A69507 /* DNSSECSupport.c in Sources */,
+                               848DA5C7165477E000D2E8B4 /* xpc_services.c in Sources */,
+                               2120ABD516B71614007089B6 /* CUPolicy.c in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                21A57F54145B2B1400939099 /* CryptoSupport.c in Sources */,
                                2124FA341471E9DE0021D7BB /* nsec.c in Sources */,
                                213BDC6E147319F400000896 /* dnssec.c in Sources */,
                                21A57F54145B2B1400939099 /* CryptoSupport.c in Sources */,
                                2124FA341471E9DE0021D7BB /* nsec.c in Sources */,
                                213BDC6E147319F400000896 /* dnssec.c in Sources */,
+                               218E8E52156D8C0300720DA0 /* dnsproxy.c in Sources */,
+                               21DED43615702C0F0060B6B9 /* DNSProxySupport.c in Sources */,
+                               216D9ACF1720C9F5008066E1 /* VPNService.c in Sources */,
+                               2127A47815C3C7B900A857FC /* nsec3.c in Sources */,
+                               21DD8FC0161E9A250033C8F8 /* anonymous.c in Sources */,
+                               21070E6016486B9000A69507 /* DNSSECSupport.c in Sources */,
+                               848DA5C8165477E000D2E8B4 /* xpc_services.c in Sources */,
+                               2120ABD616B71614007089B6 /* CUPolicy.c in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        target = 4AE471670EAFF81900A6C5AD /* dns_sd.jar */;
                        targetProxy = 4AE471690EAFF83800A6C5AD /* PBXContainerItemProxy */;
                };
                        target = 4AE471670EAFF81900A6C5AD /* dns_sd.jar */;
                        targetProxy = 4AE471690EAFF83800A6C5AD /* PBXContainerItemProxy */;
                };
+               72FB546A166D5FE40090B2D9 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = 72FB545E166D5FB00090B2D9 /* dnsctl */;
+                       targetProxy = 72FB5469166D5FE40090B2D9 /* PBXContainerItemProxy */;
+               };
+               84C5B3411665544B00C324A8 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = 84C5B3341665529800C324A8 /* dns_services */;
+                       targetProxy = 84C5B3401665544B00C324A8 /* PBXContainerItemProxy */;
+               };
                D284BF2C0ADD815A0027CCDF /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        target = D284BEBF0ADD80A20027CCDF /* dnsextd */;
                D284BF2C0ADD815A0027CCDF /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        target = D284BEBF0ADD80A20027CCDF /* dnsextd */;
                        buildSettings = {
                                ALWAYS_SEARCH_USER_PATHS = NO;
                                BUNDLE_LOADER = /usr/libexec/UserEventAgent;
                        buildSettings = {
                                ALWAYS_SEARCH_USER_PATHS = NO;
                                BUNDLE_LOADER = /usr/libexec/UserEventAgent;
+                               CODE_SIGN_IDENTITY = "-";
                                COPY_PHASE_STRIP = NO;
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_MODEL_TUNING = G5;
                                COPY_PHASE_STRIP = NO;
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_MODEL_TUNING = G5;
                                INSTALL_PATH = /System/Library/UserEventPlugins/;
                                PREBINDING = NO;
                                PRODUCT_NAME = BonjourEvents;
                                INSTALL_PATH = /System/Library/UserEventPlugins/;
                                PREBINDING = NO;
                                PRODUCT_NAME = BonjourEvents;
+                               PROVISIONING_PROFILE = "";
                                WRAPPER_EXTENSION = plugin;
                        };
                        name = Development;
                                WRAPPER_EXTENSION = plugin;
                        };
                        name = Development;
                                LD_MAP_FILE_PATH = "$(TARGET_TEMP_DIR)/$(PRODUCT_NAME)-LinkMap-$(CURRENT_VARIANT)-$(CURRENT_ARCH).txt";
                                LIBRARY_SEARCH_PATHS = "\"${CONFIGURATION_TEMP_DIR}\"";
                                MACOSX_DEPLOYMENT_TARGET = 10.5;
                                LD_MAP_FILE_PATH = "$(TARGET_TEMP_DIR)/$(PRODUCT_NAME)-LinkMap-$(CURRENT_VARIANT)-$(CURRENT_ARCH).txt";
                                LIBRARY_SEARCH_PATHS = "\"${CONFIGURATION_TEMP_DIR}\"";
                                MACOSX_DEPLOYMENT_TARGET = 10.5;
-                               "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
-                                       "$(inherited)",
-                                       "-mthumb",
-                               );
+                               "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = "$(inherited)";
                                OTHER_LDFLAGS = (
                                        "$(inherited)",
                                        "-lipsec",
                                OTHER_LDFLAGS = (
                                        "$(inherited)",
                                        "-lipsec",
                        };
                        name = Development;
                };
                        };
                        name = Development;
                };
+               72FB5466166D5FB00090B2D9 /* Development */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               ARCHS = "$(ARCHS_STANDARD_64_BIT)";
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_WARN_CONSTANT_CONVERSION = YES;
+                               CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+                               CLANG_WARN_EMPTY_BODY = YES;
+                               CLANG_WARN_INT_CONVERSION = YES;
+                               CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+                               "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "dnsctl-entitlements.plist";
+                               CODE_SIGN_IDENTITY = "-";
+                               COPY_PHASE_STRIP = NO;
+                               GCC_C_LANGUAGE_STANDARD = gnu99;
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+                               GCC_OPTIMIZATION_LEVEL = s;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "DEBUG=1",
+                                       "$(inherited)",
+                               );
+                               GCC_STRICT_ALIASING = YES;
+                               GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+                               GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES;
+                               GCC_WARN_UNUSED_VARIABLE = YES;
+                               INSTALL_PATH = /usr/bin;
+                               MACOSX_DEPLOYMENT_TARGET = 10.9;
+                               ONLY_ACTIVE_ARCH = NO;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               PROVISIONING_PROFILE = "";
+                               SDKROOT = macosx;
+                       };
+                       name = Development;
+               };
+               84C5B3371665529800C324A8 /* Development */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               ARCHS = "$(ARCHS_STANDARD_64_BIT)";
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               CLANG_WARN_EMPTY_BODY = YES;
+                               CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+                               COPY_PHASE_STRIP = NO;
+                               EXECUTABLE_PREFIX = lib;
+                               GCC_C_LANGUAGE_STANDARD = gnu99;
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+                               GCC_OPTIMIZATION_LEVEL = s;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "DEBUG=1",
+                                       "$(inherited)",
+                               );
+                               GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+                               GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES;
+                               GCC_WARN_UNUSED_VARIABLE = YES;
+                               INSTALL_PATH = /usr/lib;
+                               MACOSX_DEPLOYMENT_TARGET = 10.8;
+                               ONLY_ACTIVE_ARCH = NO;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SDKROOT = macosx;
+                       };
+                       name = Development;
+               };
                D284BE290ADD78180027CCDF /* Development */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
                D284BE290ADD78180027CCDF /* Development */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                        "-DUSE_SYSTEMCONFIGURATION_PRIVATE_HEADERS",
                                        "-fwrapv",
                                );
                                        "-DUSE_SYSTEMCONFIGURATION_PRIVATE_HEADERS",
                                        "-fwrapv",
                                );
+                               "OTHER_LDFLAGS[sdk=macosx*]" = "";
                                PREBINDING = NO;
                                STRIP_STYLE = debugging;
                                WARNING_CFLAGS = (
                                PREBINDING = NO;
                                STRIP_STYLE = debugging;
                                WARNING_CFLAGS = (
                D284BE6E0ADD80740027CCDF /* Development */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
                D284BE6E0ADD80740027CCDF /* Development */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
-                               "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "mDNSResponder-entitlements.plist";
+                               "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "mDNSResponder-entitlements.plist";
                                CODE_SIGN_IDENTITY = "-";
                                CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
                                CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
                                CODE_SIGN_IDENTITY = "-";
                                CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
                                CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
                                        "$(inherited)",
                                        "-no-cpp-precomp",
                                );
                                        "$(inherited)",
                                        "-no-cpp-precomp",
                                );
-                               "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
-                                       "$(inherited)",
-                                       "-mthumb",
-                               );
+                               "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = "$(inherited)";
                                OTHER_LDFLAGS = "";
                                "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
                                        "-Wl,-pie",
                                        "-weak_framework",
                                        DeviceToDeviceManager,
                                OTHER_LDFLAGS = "";
                                "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
                                        "-Wl,-pie",
                                        "-weak_framework",
                                        DeviceToDeviceManager,
+                                       "-lMobileGestalt",
+                                       "-lcupolicy",
                                );
                                "OTHER_LDFLAGS[sdk=macosx*][arch=*]" = (
                                        "-Wl,-pie",
                                );
                                "OTHER_LDFLAGS[sdk=macosx*][arch=*]" = (
                                        "-Wl,-pie",
                                        "-Wl,-pie",
                                        "-weak_framework",
                                        DeviceToDeviceManager,
                                        "-Wl,-pie",
                                        "-weak_framework",
                                        DeviceToDeviceManager,
+                                       "-lMobileGestalt",
+                                       "-lcupolicy",
                                );
                                "OTHER_LDFLAGS[sdk=macosx*][arch=*]" = (
                                        "-Wl,-pie",
                                );
                                "OTHER_LDFLAGS[sdk=macosx*][arch=*]" = (
                                        "-Wl,-pie",
                                CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
                                CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
-                               HEADER_SEARCH_PATHS = ../mDNSShared;
+                               HEADER_SEARCH_PATHS = (
+                                       ../mDNSShared,
+                                       "${SYSTEM_LIBRARY_DIR}/Frameworks/System.framework/PrivateHeaders",
+                               );
                                INSTALL_PATH = /usr/bin;
                                OTHER_CFLAGS = "-no-cpp-precomp";
                                OTHER_LDFLAGS = "";
                                INSTALL_PATH = /usr/bin;
                                OTHER_CFLAGS = "-no-cpp-precomp";
                                OTHER_LDFLAGS = "";
                                MACOSX_DEPLOYMENT_TARGET = 10.5;
                                OTHER_CFLAGS = "";
                                OTHER_LDFLAGS = "";
                                MACOSX_DEPLOYMENT_TARGET = 10.5;
                                OTHER_CFLAGS = "";
                                OTHER_LDFLAGS = "";
+                               "OTHER_LDFLAGS[sdk=macosx*]" = "-Wl,-pie";
                                OTHER_REZFLAGS = "";
                                PRODUCT_NAME = ddnswriteconfig;
                                REZ_EXECUTABLE = YES;
                                OTHER_REZFLAGS = "";
                                PRODUCT_NAME = ddnswriteconfig;
                                REZ_EXECUTABLE = YES;
                                CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
                                CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
                                COPY_PHASE_STRIP = NO;
                                CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
                                CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
                                COPY_PHASE_STRIP = NO;
+                               DYLIB_CURRENT_VERSION = "$(RC_ProjectSourceVersion)";
                                EXECUTABLE_EXTENSION = dylib;
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
                                EXECUTABLE_EXTENSION = dylib;
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
                                HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/";
                                INSTALL_PATH = /usr/lib/system;
                                "INSTALL_PATH[sdk=iphonesimulator*]" = "$(SDKROOT)/usr/lib/system";
                                HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/";
                                INSTALL_PATH = /usr/lib/system;
                                "INSTALL_PATH[sdk=iphonesimulator*]" = "$(SDKROOT)/usr/lib/system";
+                               LINK_WITH_STANDARD_LIBRARIES = NO;
                                OTHER_LDFLAGS = (
                                OTHER_LDFLAGS = (
-                                       "-umbrella",
-                                       System,
+                                       "-Wl,-umbrella,System",
+                                       "-L/usr/lib/system",
+                                       "-ldyld",
+                                       "-lcompiler_rt",
+                                       "-lsystem_kernel",
+                                       "-lsystem_platform",
+                                       "-lsystem_pthread",
+                                       "-lsystem_malloc",
+                                       "-lsystem_c",
+                                       "-lsystem_blocks",
+                                       "-ldispatch",
+                                       "-llaunch",
+                                       "-lsystem_asl",
+                               );
+                               "OTHER_LDFLAGS[sdk=iphonesimulator*]" = (
+                                       "-Wl,-umbrella,System",
+                                       "-L/usr/lib/system",
+                                       "-ldyld_sim",
+                                       "-lcompiler_rt_sim",
+                                       "-lsystem_sim_c",
+                                       "-lsystem_sim_blocks",
+                                       "-ldispatch",
+                                       "-Wl,-upward-lSystem",
                                );
                                );
-                               "OTHER_LDFLAGS[sdk=iphonesimulator*][arch=*]" = "";
                                PRODUCT_NAME = libsystem_dnssd_debug;
                                "PRODUCT_NAME[sdk=iphonesimulator*]" = libsystem_sim_dnssd_debug;
                        };
                                PRODUCT_NAME = libsystem_dnssd_debug;
                                "PRODUCT_NAME[sdk=iphonesimulator*]" = libsystem_sim_dnssd_debug;
                        };
                                CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
                                CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
                                COPY_PHASE_STRIP = NO;
                                CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
                                CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
                                COPY_PHASE_STRIP = NO;
+                               DYLIB_CURRENT_VERSION = "$(RC_ProjectSourceVersion)";
                                EXECUTABLE_EXTENSION = dylib;
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
                                EXECUTABLE_EXTENSION = dylib;
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
                                HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/";
                                INSTALL_PATH = /usr/lib/system;
                                "INSTALL_PATH[sdk=iphonesimulator*]" = "$(SDKROOT)/usr/lib/system";
                                HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/";
                                INSTALL_PATH = /usr/lib/system;
                                "INSTALL_PATH[sdk=iphonesimulator*]" = "$(SDKROOT)/usr/lib/system";
+                               LINK_WITH_STANDARD_LIBRARIES = NO;
                                OTHER_LDFLAGS = (
                                OTHER_LDFLAGS = (
-                                       "-umbrella",
-                                       System,
+                                       "-Wl,-umbrella,System",
+                                       "-L/usr/lib/system",
+                                       "-ldyld",
+                                       "-lcompiler_rt",
+                                       "-lsystem_kernel",
+                                       "-lsystem_platform",
+                                       "-lsystem_pthread",
+                                       "-lsystem_malloc",
+                                       "-lsystem_c",
+                                       "-lsystem_blocks",
+                                       "-ldispatch",
+                                       "-llaunch",
+                                       "-lsystem_asl",
+                               );
+                               "OTHER_LDFLAGS[sdk=iphonesimulator*]" = (
+                                       "-Wl,-umbrella,System",
+                                       "-L/usr/lib/system",
+                                       "-ldyld_sim",
+                                       "-lcompiler_rt_sim",
+                                       "-lsystem_sim_c",
+                                       "-lsystem_sim_blocks",
+                                       "-ldispatch",
+                                       "-Wl,-upward-lSystem",
                                );
                                );
-                               "OTHER_LDFLAGS[sdk=iphonesimulator*][arch=*]" = "";
                                PRODUCT_NAME = libsystem_dnssd_profile;
                                "PRODUCT_NAME[sdk=iphonesimulator*]" = libsystem_sim_dnssd_profile;
                        };
                                PRODUCT_NAME = libsystem_dnssd_profile;
                                "PRODUCT_NAME[sdk=iphonesimulator*]" = libsystem_sim_dnssd_profile;
                        };
                                CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
                                CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
                                COPY_PHASE_STRIP = NO;
                                CONFIGURATION_BUILD_DIR = "${BUILD_DIR}";
                                CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build";
                                COPY_PHASE_STRIP = NO;
+                               DYLIB_CURRENT_VERSION = "$(RC_ProjectSourceVersion)";
                                EXECUTABLE_EXTENSION = dylib;
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
                                EXECUTABLE_EXTENSION = dylib;
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
                                INSTALLHDRS_SCRIPT_PHASE = YES;
                                INSTALL_PATH = /usr/lib/system;
                                "INSTALL_PATH[sdk=iphonesimulator*]" = "$(SDKROOT)/usr/lib/system";
                                INSTALLHDRS_SCRIPT_PHASE = YES;
                                INSTALL_PATH = /usr/lib/system;
                                "INSTALL_PATH[sdk=iphonesimulator*]" = "$(SDKROOT)/usr/lib/system";
+                               LINK_WITH_STANDARD_LIBRARIES = NO;
                                OTHER_LDFLAGS = (
                                OTHER_LDFLAGS = (
-                                       "-umbrella",
-                                       System,
+                                       "-Wl,-umbrella,System",
+                                       "-L/usr/lib/system",
+                                       "-ldyld",
+                                       "-lcompiler_rt",
+                                       "-lsystem_kernel",
+                                       "-lsystem_platform",
+                                       "-lsystem_pthread",
+                                       "-lsystem_malloc",
+                                       "-lsystem_c",
+                                       "-lsystem_blocks",
+                                       "-ldispatch",
+                                       "-llaunch",
+                                       "-lsystem_asl",
+                               );
+                               "OTHER_LDFLAGS[sdk=iphonesimulator*]" = (
+                                       "-Wl,-umbrella,System",
+                                       "-L/usr/lib/system",
+                                       "-ldyld_sim",
+                                       "-lcompiler_rt_sim",
+                                       "-lsystem_sim_c",
+                                       "-lsystem_sim_blocks",
+                                       "-ldispatch",
+                                       "-Wl,-upward-lSystem",
                                );
                                );
-                               "OTHER_LDFLAGS[sdk=iphonesimulator*][arch=*]" = "";
                                PRODUCT_NAME = libsystem_dnssd;
                                "PRODUCT_NAME[sdk=iphonesimulator*]" = libsystem_sim_dnssd;
                        };
                                PRODUCT_NAME = libsystem_dnssd;
                                "PRODUCT_NAME[sdk=iphonesimulator*]" = libsystem_sim_dnssd;
                        };
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Development;
                };
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Development;
                };
+               72FB5465166D5FB00090B2D9 /* Build configuration list for PBXNativeTarget "dnsctl" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               72FB5466166D5FB00090B2D9 /* Development */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Development;
+               };
+               84C5B3361665529800C324A8 /* Build configuration list for PBXNativeTarget "dns_services" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               84C5B3371665529800C324A8 /* Development */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Development;
+               };
                D284BE280ADD78180027CCDF /* Build configuration list for PBXAggregateTarget "Build More" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
                D284BE280ADD78180027CCDF /* Build configuration list for PBXAggregateTarget "Build More" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
diff --git a/mDNSMacOSX/mDNSResponderLogging.mobileconfig b/mDNSMacOSX/mDNSResponderLogging.mobileconfig
new file mode 100644 (file)
index 0000000..34ec0d9
--- /dev/null
@@ -0,0 +1,57 @@
+<?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>PayloadIdentifier</key>
+    <string>com.apple.mDNSResponder</string>
+    <key>PayloadUUID</key>
+    <string>6D0962E1-558A-44FB-8FE0-1F4F0BD157F4</string>
+    <key>PayloadDescription</key>
+    <string>Turns on mDNSResponder Debug Logging</string>
+    <key>PayloadDisplayName</key>
+    <string>mDNSResponder Debug Logging</string>
+    <key>PayloadOrganization</key>
+    <string>Apple, Inc</string>
+    <key>PayloadType</key>
+    <string>Configuration</string>
+    <key>PayloadVersion</key>
+    <integer>2</integer>
+
+    <key>ConsentText</key>
+    <dict>
+      <key>en</key>
+      <string>English consent text</string>
+      <key>jp</key>
+      <string>Japanese consent text</string>
+      <key>default</key>
+      <string>Default consent text - used if none of the other languages match</string>
+    </dict>
+
+    <key>PayloadContent</key>
+    <array>
+        <dict>
+            <key>PayloadUUID</key>
+            <string>6D0962E1-558A-44FB-8FE0-1F4F0BD157F4</string>
+            <key>PayloadIdentifier</key>
+            <string>com.apple.defaults.1</string>
+            <key>PayloadType</key>
+            <string>com.apple.defaults.managed</string>
+            <key>PayloadVersion</key>
+            <integer>1</integer>
+            <key>PayloadContent</key>
+            <array>
+                <dict>
+                    <key>DefaultsDomainName</key>
+                    <string>com.apple.mDNSResponder</string>
+                    <key>DefaultsData</key>
+                    <dict>
+                        <key>EnableLogging</key>
+                        <true/>
+                    </dict>
+                </dict>
+            </array>
+        </dict>
+    </array>
+</dict>
+</plist>
+
index 1b43c077081c64293418ea738015c9e00d8e59e2..c0badf43af0632d7b3bb699ca555784bc0feb76c 100755 (executable)
@@ -196,7 +196,7 @@ int main(int argc, char **argv)
         MakeDomainNameFromDNSNameString(&type, gServiceType);
         MakeDomainNameFromDNSNameString(&domain, gServiceDomain);
 
         MakeDomainNameFromDNSNameString(&type, gServiceType);
         MakeDomainNameFromDNSNameString(&domain, gServiceDomain);
 
-        status = mDNS_StartBrowse(&mDNSStorage, &question, &type, &domain, mDNSInterface_Any, 0, mDNSfalse, mDNSfalse, BrowseCallback, NULL);
+        status = mDNS_StartBrowse(&mDNSStorage, &question, &type, &domain, mDNSNULL, mDNSInterface_Any, 0, mDNSfalse, mDNSfalse, BrowseCallback, NULL);
 
         // Run the platform main event loop until the user types ^C.
         // The BrowseCallback routine is responsible for printing
 
         // Run the platform main event loop until the user types ^C.
         // The BrowseCallback routine is responsible for printing
index 745ff21734044b4403c49dfbe18164548b6651a5..003ac631c8094f2a355fe842aeb65302b49ea690 100644 (file)
@@ -217,8 +217,11 @@ mDNSlocal mStatus StartQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const m
     q->ValidationRequired = 0;
     q->ValidatingResponse = 0;
     q->WakeOnResolve    = 0;
     q->ValidationRequired = 0;
     q->ValidatingResponse = 0;
     q->WakeOnResolve    = 0;
-    q->UseBrackgroundTrafficClass = mDNSfalse;
+    q->UseBackgroundTrafficClass = mDNSfalse;
+    q->ProxyQuestion    = 0;
     q->qnameOrig        = mDNSNULL;
     q->qnameOrig        = mDNSNULL;
+    q->AnonInfo         = mDNSNULL;
+    q->pid              = mDNSPlatformGetPID();
     q->QuestionCallback = callback;
     q->QuestionContext  = NULL;
 
     q->QuestionCallback = callback;
     q->QuestionContext  = NULL;
 
index b9ec10b73d57b68bd007de2482e254e19e3a19b7..d095a0f456a195f967b1a2157b3b22d7f811218c 100755 (executable)
@@ -235,7 +235,7 @@ clean:
 DAEMONOBJS = $(OBJDIR)/PosixDaemon.c.o $(OBJDIR)/mDNSPosix.c.o $(OBJDIR)/mDNSUNP.c.o $(OBJDIR)/mDNS.c.o \
              $(OBJDIR)/DNSDigest.c.o $(OBJDIR)/uDNS.c.o $(OBJDIR)/DNSCommon.c.o $(OBJDIR)/uds_daemon.c.o \
              $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/dnssd_ipc.c.o $(OBJDIR)/GenLinkedList.c.o $(OBJDIR)/PlatformCommon.c.o \
 DAEMONOBJS = $(OBJDIR)/PosixDaemon.c.o $(OBJDIR)/mDNSPosix.c.o $(OBJDIR)/mDNSUNP.c.o $(OBJDIR)/mDNS.c.o \
              $(OBJDIR)/DNSDigest.c.o $(OBJDIR)/uDNS.c.o $(OBJDIR)/DNSCommon.c.o $(OBJDIR)/uds_daemon.c.o \
              $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/dnssd_ipc.c.o $(OBJDIR)/GenLinkedList.c.o $(OBJDIR)/PlatformCommon.c.o \
-                        $(OBJDIR)/CryptoAlg.c.o
+                        $(OBJDIR)/CryptoAlg.c.o $(OBJDIR)/anonymous.c.o
 
 # dnsextd target build dnsextd
 DNSEXTDOBJ = $(OBJDIR)/mDNSPosix.c.o $(OBJDIR)/mDNSUNP.c.o $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/GenLinkedList.c.o $(OBJDIR)/DNSDigest.c.o \
 
 # dnsextd target build dnsextd
 DNSEXTDOBJ = $(OBJDIR)/mDNSPosix.c.o $(OBJDIR)/mDNSUNP.c.o $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/GenLinkedList.c.o $(OBJDIR)/DNSDigest.c.o \
@@ -446,7 +446,7 @@ JavaDoc: Java setup
 # The following targets build embedded example programs
 SPECIALOBJ = $(OBJDIR)/mDNSPosix.c.o $(OBJDIR)/mDNSUNP.c.o $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/GenLinkedList.c.o \
        $(OBJDIR)/DNSDigest.c.o $(OBJDIR)/uDNS.c.o $(OBJDIR)/DNSCommon.c.o $(OBJDIR)/PlatformCommon.c.o \
 # The following targets build embedded example programs
 SPECIALOBJ = $(OBJDIR)/mDNSPosix.c.o $(OBJDIR)/mDNSUNP.c.o $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/GenLinkedList.c.o \
        $(OBJDIR)/DNSDigest.c.o $(OBJDIR)/uDNS.c.o $(OBJDIR)/DNSCommon.c.o $(OBJDIR)/PlatformCommon.c.o \
-       $(OBJDIR)/CryptoAlg.c.o
+       $(OBJDIR)/CryptoAlg.c.o $(OBJDIR)/anonymous.c.o
 COMMONOBJ  = $(SPECIALOBJ) $(OBJDIR)/mDNS.c.o
 APPOBJ     = $(COMMONOBJ) $(OBJDIR)/ExampleClientApp.c.o
 
 COMMONOBJ  = $(SPECIALOBJ) $(OBJDIR)/mDNS.c.o
 APPOBJ     = $(COMMONOBJ) $(OBJDIR)/ExampleClientApp.c.o
 
index 7b23fc306b3b20c55a0a147c51fe27cd5afdf777..1354f1f83ce59764fb8ddc9b1b6c0d851698adc7 100644 (file)
@@ -725,8 +725,9 @@ mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const
         const mDNSu8 *ep = ptr;
         ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &pkt);
         if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; }
         const mDNSu8 *ep = ptr;
         ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &pkt);
         if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; }
-        mprintf("%#-16a (?)  **** ERROR: SHOULD NOT HAVE AUTHORITY IN mDNS RESPONSE **** %-5s %##s\n",
-                srcaddr, DNSTypeName(pkt.r.resrec.rrtype), pkt.r.resrec.name->c);
+        if (pkt.r.resrec.rrtype != kDNSType_NSEC3)
+            mprintf("%#-16a (?)  **** ERROR: SHOULD NOT HAVE AUTHORITY IN mDNS RESPONSE **** %-5s %##s\n",
+                    srcaddr, DNSTypeName(pkt.r.resrec.rrtype), pkt.r.resrec.name->c);
     }
 
     for (i=0; i<msg->h.numAdditionals; i++)
     }
 
     for (i=0; i<msg->h.numAdditionals; i++)
index d2869d4d238948ff156c3f0cf99ce233d37166b0..9af8ebc01ec86d0442a9b3bd98fe3ac8b39e6291 100755 (executable)
@@ -309,6 +309,13 @@ mDNSlocal void SocketDataReady(mDNS *const m, PosixNetworkInterface *intf, int s
                         &senderAddr, senderPort, &destAddr, MulticastDNSPort, InterfaceID);
 }
 
                         &senderAddr, senderPort, &destAddr, MulticastDNSPort, InterfaceID);
 }
 
+mDNSexport mDNSBool mDNSPlatformPeekUDP(mDNS *const m, UDPSocket *src)
+{
+    (void)m;    // unused
+    (void)src;  // unused
+    return mDNSfalse;
+}
+
 mDNSexport TCPSocket *mDNSPlatformTCPSocket(mDNS * const m, TCPSocketFlags flags, mDNSIPPort * port, mDNSBool useBackgroundTrafficClass)
 {
     (void)m;            // Unused
 mDNSexport TCPSocket *mDNSPlatformTCPSocket(mDNS * const m, TCPSocketFlags flags, mDNSIPPort * port, mDNSBool useBackgroundTrafficClass)
 {
     (void)m;            // Unused
@@ -432,7 +439,8 @@ mDNSexport void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result
 #pragma mark ***** DDNS Config Platform Functions
 #endif
 
 #pragma mark ***** DDNS Config Platform Functions
 #endif
 
-mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **RegDomains, DNameListElem **BrowseDomains)
+mDNSexport mDNSBool mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **RegDomains,
+    DNameListElem **BrowseDomains, mDNSBool ackConfig)
 {
     (void) m;
     (void) setservers;
 {
     (void) m;
     (void) setservers;
@@ -440,6 +448,9 @@ mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDN
     (void) setsearch;
     (void) RegDomains;
     (void) BrowseDomains;
     (void) setsearch;
     (void) RegDomains;
     (void) BrowseDomains;
+    (void) ackConfig;
+
+    return mDNStrue;
 }
 
 mDNSexport mStatus mDNSPlatformGetPrimaryInterface(mDNS * const m, mDNSAddr * v4, mDNSAddr * v6, mDNSAddr * router)
 }
 
 mDNSexport mStatus mDNSPlatformGetPrimaryInterface(mDNS * const m, mDNSAddr * v4, mDNSAddr * v6, mDNSAddr * router)
@@ -499,7 +510,7 @@ mDNSexport int ParseDNSServers(mDNS *m, const char *filePath)
             mDNSAddr DNSAddr;
             DNSAddr.type = mDNSAddrType_IPv4;
             DNSAddr.ip.v4.NotAnInteger = ina.s_addr;
             mDNSAddr DNSAddr;
             DNSAddr.type = mDNSAddrType_IPv4;
             DNSAddr.ip.v4.NotAnInteger = ina.s_addr;
-            mDNS_AddDNSServer(m, NULL, mDNSInterface_Any, &DNSAddr, UnicastDNSPort, mDNSfalse, 0, mDNSfalse, 0);
+            mDNS_AddDNSServer(m, NULL, mDNSInterface_Any, 0, &DNSAddr, UnicastDNSPort, kScopeNone, 0, mDNSfalse, 0, mDNStrue, mDNStrue, mDNSfalse);
             numOfServers++;
         }
     }
             numOfServers++;
         }
     }
@@ -1362,6 +1373,38 @@ mDNSexport mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, C
     return mDNSfalse;
 }
 
     return mDNSfalse;
 }
 
+mDNSexport void BumpDNSSECStats(mDNS *const m, DNSSECStatsAction action, DNSSECStatsType type, mDNSu32 value)
+{
+    (void)m;
+    (void)action;
+    (void)type;
+    (void)value;
+}
+
+// Proxy stub functions
+mDNSexport mDNSu8 *DNSProxySetAttributes(DNSQuestion *q, DNSMessageHeader *h, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *limit)
+{
+    (void) q;
+    (void) h;
+    (void) msg;
+    (void) ptr;
+    (void) limit;
+
+    return ptr;
+}
+
+mDNSexport void DNSProxyInit(mDNS *const m, mDNSu32 IpIfArr[], mDNSu32 OpIf)
+{
+    (void) m;
+    (void) IpIfArr;
+    (void) OpIf;
+}
+
+mDNSexport void DNSProxyTerminate(mDNS *const m)
+{
+    (void) m;
+}
+
 // mDNS core calls this routine to clear blocks of memory.
 // On the Posix platform this is a simple wrapper around ANSI C memset.
 mDNSexport void    mDNSPlatformMemZero(void *dst, mDNSu32 len)
 // mDNS core calls this routine to clear blocks of memory.
 // On the Posix platform this is a simple wrapper around ANSI C memset.
 mDNSexport void    mDNSPlatformMemZero(void *dst, mDNSu32 len)
@@ -1462,6 +1505,62 @@ mDNSexport mStatus mDNSPlatformRetrieveTCPInfo(mDNS *const m, mDNSAddr *laddr, m
     return mStatus_NoError;
 }
 
     return mStatus_NoError;
 }
 
+mDNSexport mStatus mDNSPlatformGetRemoteMacAddr(mDNS *const m, mDNSAddr *raddr)
+{
+    (void) raddr; // Unused
+    (void) m;     // Unused
+
+    return mStatus_NoError;
+}
+
+mDNSexport mStatus    mDNSPlatformStoreSPSMACAddr(mDNSAddr *spsaddr, char *ifname)
+{
+    (void) spsaddr; // Unused
+    (void) ifname;  // Unused
+
+    return mStatus_NoError;
+}
+
+mDNSexport mDNSu16 mDNSPlatformGetUDPPort(UDPSocket *sock)
+{
+    (void) sock; // unused
+    return (mDNSu16)-1;
+}
+
+mDNSexport mDNSBool mDNSPlatformInterfaceIsD2D(mDNSInterfaceID InterfaceID)
+{
+    (void) InterfaceID; // unused
+    
+    return mDNSfalse;
+}
+
+mDNSexport mDNSBool mDNSPlatformAllowPID(mDNS *const m, DNSQuestion *q)
+{
+    (void) m;
+    (void) q;
+    return mDNStrue;
+}
+
+mDNSexport mDNSs32 mDNSPlatformGetServiceID(mDNS *const m, DNSQuestion *q)
+{
+    (void) m;
+    (void) q;
+    return 0;
+}
+
+mDNSexport void mDNSPlatformSetDelegatePID(UDPSocket *src, const mDNSAddr *dst, DNSQuestion *q)
+{
+    (void) src;
+    (void) dst;
+    (void) q;
+}
+
+mDNSexport mDNSs32 mDNSPlatformGetPID()
+{
+    return 0;
+}
+
 mDNSlocal void mDNSPosixAddToFDSet(int *nfds, fd_set *readfds, int s)
 {
     if (*nfds < s + 1) *nfds = s + 1;
 mDNSlocal void mDNSPosixAddToFDSet(int *nfds, fd_set *readfds, int s)
 {
     if (*nfds < s + 1) *nfds = s + 1;
index f650c10d00c61eaf4553ab5df3eeadb4ff9e734c..d86a755aeee8a4ed7781af75f659904ef444709c 100644 (file)
@@ -190,7 +190,10 @@ mDNSexport void mDNSPlatformWriteLogMsg(const char *ident, const char *buffer, m
         if (ident && ident[0] && mDNSPlatformClockDivisor)
             syslog(syslog_level, "%8d.%03d: %s", (int)(t/1000), ms, buffer);
         else
         if (ident && ident[0] && mDNSPlatformClockDivisor)
             syslog(syslog_level, "%8d.%03d: %s", (int)(t/1000), ms, buffer);
         else
-#endif
+#elif APPLE_OSX_mDNSResponder
+        mDNSPlatformLogToFile(syslog_level, buffer);
+#else
         syslog(syslog_level, "%s", buffer);
         syslog(syslog_level, "%s", buffer);
+#endif
     }
 }
     }
 }
index cfcdff9f01ff6615da1c6cb2206d04a03436e8ae..bc42582734110983472662f744a19ca08cbc4fc6 100644 (file)
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 4 -*-
  *
 /* -*- Mode: C; tab-width: 4 -*-
  *
- * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2003-2013 Apple Computer, Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -77,7 +77,7 @@
  */
 
 #ifndef _DNS_SD_H
  */
 
 #ifndef _DNS_SD_H
-#define _DNS_SD_H 3793801
+#define _DNS_SD_H 5220111
 
 #ifdef  __cplusplus
 extern "C" {
 
 #ifdef  __cplusplus
 extern "C" {
@@ -167,6 +167,12 @@ struct sockaddr;
  * The reliable way to test whether a particular bit is set is not with an equality test,
  * but with a bitwise mask:
  *     if (flags & kDNSServiceFlagsAdd) ...
  * The reliable way to test whether a particular bit is set is not with an equality test,
  * but with a bitwise mask:
  *     if (flags & kDNSServiceFlagsAdd) ...
+ * With the exception of kDNSServiceFlagsValidate, each flag can be valid(be set) 
+ * EITHER only as an input to one of the DNSService*() APIs OR only as an output
+ * (provide status) through any of the callbacks used. For example, kDNSServiceFlagsAdd
+ * can be set only as an output in the callback, whereas the kDNSServiceFlagsIncludeP2P
+ * can be set only as an input to the DNSService*() APIs. See comments on kDNSServiceFlagsValidate  
+ * defined in enum below.
  */
 enum
 {
  */
 enum
 {
@@ -231,14 +237,13 @@ enum
      * DNS, even for a name in a domain (e.g. foo.apple.com.) that would normally imply unicast DNS.
      */
 
      * DNS, even for a name in a domain (e.g. foo.apple.com.) that would normally imply unicast DNS.
      */
 
-    kDNSServiceFlagsForce               = 0x800,
-    /* Flag for signifying a "stronger" variant of an operation.
-     * Currently defined only for DNSServiceReconfirmRecord(), where it forces a record to
-     * be removed from the cache immediately, instead of querying for a few seconds before
-     * concluding that the record is no longer valid and then removing it. This flag should
-     * be used with caution because if a service browsing PTR record is indeed still valid
-     * on the network, forcing its removal will result in a user-interface flap -- the
-     * discovered service instance will disappear, and then re-appear moments later.
+    kDNSServiceFlagsForce               = 0x800,    // This flag is deprecated.
+
+    kDNSServiceFlagsKnownUnique         = 0x800,
+    /* 
+     * Client guarantees that record names are unique, so we can skip sending out initial
+     * probe messages.  Standard name conflict resolution is still done if a conflict is discovered.
+     * Currently only valid for a DNSServiceRegister call.
      */
 
     kDNSServiceFlagsReturnIntermediates = 0x1000,
      */
 
     kDNSServiceFlagsReturnIntermediates = 0x1000,
@@ -380,19 +385,130 @@ enum
     * class for packets that service the request.
     */
 
     * class for packets that service the request.
     */
 
-    kDNSServiceFlagsIncludeAWDL      = 0x100000
-     /*
-      * Include AWDL interface when kDNSServiceInterfaceIndexAny is specified.
+    kDNSServiceFlagsIncludeAWDL      = 0x100000,
+   /*
+    * Include AWDL interface when kDNSServiceInterfaceIndexAny is specified.
+    */
+
+    kDNSServiceFlagsValidate               = 0x200000,
+   /*
+    * This flag is meaningful in DNSServiceGetAddrInfo and DNSServiceQueryRecord. This is the ONLY flag to be valid 
+    * as an input to the APIs and also an output through the callbacks in the APIs.
+    *
+    * When this flag is passed to DNSServiceQueryRecord and DNSServiceGetAddrInfo to resolve unicast names, 
+    * the response  will be validated using DNSSEC. The validation results are delivered using the flags field in 
+    * the callback and kDNSServiceFlagsValidate is marked in the flags to indicate that DNSSEC status is also available.
+    * When the callback is called to deliver the query results, the validation results may or may not be available. 
+    * If it is not delivered along with the results, the validation status is delivered when the validation completes.
+    * 
+    * When the validation results are delivered in the callback, it is indicated by marking the flags with
+    * kDNSServiceFlagsValidate and kDNSServiceFlagsAdd along with the DNSSEC status flags (described below) and a NULL
+    * sockaddr will be returned for DNSServiceGetAddrInfo and zero length rdata will be returned for DNSServiceQueryRecord.
+    * DNSSEC validation results are for the whole RRSet and not just individual records delivered in the callback. When
+    * kDNSServiceFlagsAdd is not set in the flags, applications should implicitly assume that the DNSSEC status of the 
+    * RRSet that has been delivered up until that point is not valid anymore, till another callback is called with
+    * kDNSServiceFlagsAdd and kDNSServiceFlagsValidate.
+    *
+    * The following four flags indicate the status of the DNSSEC validation and marked in the flags field of the callback.
+    * When any of the four flags is set, kDNSServiceFlagsValidate will also be set. To check the validation status, the 
+    * other applicable output flags should be masked. See kDNSServiceOutputFlags below.
+    */
+
+    kDNSServiceFlagsSecure                 = 0x200010,
+   /*
+    * The response has been validated by verifying all the signaures in the response and was able to
+    * build a successful authentication chain starting from a known trust anchor.   
+    */
+
+    kDNSServiceFlagsInsecure               = 0x200020,
+   /*
+    * A chain of trust cannot be built starting from a known trust anchor to the response.
+    */
+
+    kDNSServiceFlagsBogus                  = 0x200040,
+   /*
+    * If the response cannot be verified to be secure due to expired signatures, missing signatures etc.,
+    * then the results are considered to be bogus.
+    */
+
+    kDNSServiceFlagsIndeterminate          = 0x200080,
+   /*
+    * There is no valid trust anchor that can be used to determine whether a response is secure or not.
+    */
+
+    kDNSServiceFlagsUnicastResponse        = 0x400000,
+   /*
+    * Request unicast response to query.
+    */
+    kDNSServiceFlagsValidateOptional       = 0x800000,
+
+    /*
+     * This flag is identical to kDNSServiceFlagsValidate except for the case where the response
+     * cannot be validated. If this flag is set in DNSServiceQueryRecord or DNSServiceGetAddrInfo,
+     * the DNSSEC records will be requested for validation. If they cannot be received for some reason
+     * during the validation (e.g., zone is not signed, zone is signed but cannot be traced back to
+     * root, recursive server does not understand DNSSEC etc.), then this will fallback to the default
+     * behavior where the validation will not be performed and no DNSSEC results will be provided.
+     *
+     * If the zone is signed and there is a valid path to a known trust anchor configured in the system
+     * and the application requires DNSSEC validation irrespective of the DNSSEC awareness in the current
+     * network, then this option MUST not be used. This is only intended to be used during the transition
+     * period where the different nodes participating in the DNS resolution may not understand DNSSEC or
+     * managed properly (e.g. missing DS record) but still want to be able to resolve DNS successfully.
+     */
+
+    kDNSServiceFlagsWakeOnlyService        = 0x1000000,
+    /*
+     * This flag is meaningful only in DNSServiceRegister. When set, the service will not be registered
+     * with sleep proxy server during sleep.
+     */
+
+    kDNSServiceFlagsThresholdOne           = 0x2000000,
+    kDNSServiceFlagsThresholdFinder        = 0x4000000,
+    kDNSServiceFlagsThresholdReached       = kDNSServiceFlagsThresholdOne,
+    /*
+     * kDNSServiceFlagsThresholdOne is meaningful only in DNSServiceBrowse. When set,
+     * the system will stop issuing browse queries on the network once the number
+     * of answers returned is one or more.  It will issue queries on the network
+     * again if the number of answers drops to zero.
+     * This flag is for Apple internal use only. Third party developers
+     * should not rely on this behavior being supported in any given software release.
+     *
+     * kDNSServiceFlagsThresholdFinder is meaningful only in DNSServiceBrowse. When set,
+     * the system will stop issuing browse queries on the network once the number
+     * of answers has reached the threshold set for Finder.
+     * It will issue queries on the network again if the number of answers drops below
+     * this threshold.
+     * This flag is for Apple internal use only. Third party developers
+     * should not rely on this behavior being supported in any given software release.
+     *
+     * When kDNSServiceFlagsThresholdReached is set in the client callback add or remove event,
+     * it indicates that the browse answer threshold has been reached and no 
+     * browse requests will be generated on the network until the number of answers falls
+     * below the threshold value.  Add and remove events can still occur based
+     * on incoming Bonjour traffic observed by the system.
+     * The set of services return to the client is not guaranteed to represent the 
+     * entire set of services present on the network once the threshold has been reached.
+     *
+     * Note, while kDNSServiceFlagsThresholdReached and kDNSServiceFlagsThresholdOne
+     * have the same value, there  isn't a conflict because kDNSServiceFlagsThresholdReached
+     * is only set in the callbacks and kDNSServiceFlagsThresholdOne is only set on
+     * input to a DNSServiceBrowse call.
      */
 };
 
      */
 };
 
-/* Possible protocols for DNSServiceNATPortMappingCreate(). */
+#define kDNSServiceOutputFlags (kDNSServiceFlagsValidate | kDNSServiceFlagsValidateOptional | kDNSServiceFlagsMoreComing | kDNSServiceFlagsAdd | kDNSServiceFlagsDefault)
+   /* All the output flags excluding the DNSSEC Status flags. Typically used to check DNSSEC Status */
+
+/* Possible protocol values */
 enum
 {
 enum
 {
+    /* for DNSServiceGetAddrInfo() */
     kDNSServiceProtocol_IPv4 = 0x01,
     kDNSServiceProtocol_IPv6 = 0x02,
     /* 0x04 and 0x08 reserved for future internetwork protocols */
 
     kDNSServiceProtocol_IPv4 = 0x01,
     kDNSServiceProtocol_IPv6 = 0x02,
     /* 0x04 and 0x08 reserved for future internetwork protocols */
 
+    /* for DNSServiceNATPortMappingCreate() */
     kDNSServiceProtocol_UDP  = 0x10,
     kDNSServiceProtocol_TCP  = 0x20
                                /* 0x40 and 0x80 reserved for future transport protocols, e.g. SCTP [RFC 2960]
     kDNSServiceProtocol_UDP  = 0x10,
     kDNSServiceProtocol_TCP  = 0x20
                                /* 0x40 and 0x80 reserved for future transport protocols, e.g. SCTP [RFC 2960]
@@ -518,8 +634,8 @@ enum
     kDNSServiceErr_BadKey                    = -65561,
     kDNSServiceErr_Transient                 = -65562,
     kDNSServiceErr_ServiceNotRunning         = -65563,  /* Background daemon not running */
     kDNSServiceErr_BadKey                    = -65561,
     kDNSServiceErr_Transient                 = -65562,
     kDNSServiceErr_ServiceNotRunning         = -65563,  /* Background daemon not running */
-    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_NATPortMappingUnsupported = -65564,  /* NAT doesn't support PCP, NAT-PMP or UPnP */
+    kDNSServiceErr_NATPortMappingDisabled    = -65565,  /* NAT supports PCP, 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_Timeout                   = -65568
     kDNSServiceErr_NoRouter                  = -65566,  /* No router currently configured (probably no network connectivity) */
     kDNSServiceErr_PollingMode               = -65567,
     kDNSServiceErr_Timeout                   = -65568
@@ -620,12 +736,16 @@ enum
  * to their DNSServiceBrowseReply() callback function, and discarding those
  * where the interface index is not kDNSServiceInterfaceIndexLocalOnly.
  *
  * to their DNSServiceBrowseReply() callback function, and discarding those
  * where the interface index is not kDNSServiceInterfaceIndexLocalOnly.
  *
- * kDNSServiceInterfaceIndexP2P is meaningful only in Browse, QueryRecord,
+ * kDNSServiceInterfaceIndexP2P is meaningful only in Browse, QueryRecord, Register,
  * and Resolve operations. It should not be used in other DNSService APIs.
  *
  * - If kDNSServiceInterfaceIndexP2P is passed to DNSServiceBrowse or
  *   DNSServiceQueryRecord, it restricts the operation to P2P.
  *
  * and Resolve operations. It should not be used in other DNSService APIs.
  *
  * - If kDNSServiceInterfaceIndexP2P is passed to DNSServiceBrowse or
  *   DNSServiceQueryRecord, it restricts the operation to P2P.
  *
+ * - If kDNSServiceInterfaceIndexP2P is passed to DNSServiceRegister, it is
+ *   mapped internally to kDNSServiceInterfaceIndexAny with the kDNSServiceFlagsIncludeP2P
+ *   set.
+ *
  * - If kDNSServiceInterfaceIndexP2P is passed to DNSServiceResolve, it is
  *   mapped internally to kDNSServiceInterfaceIndexAny with the kDNSServiceFlagsIncludeP2P
  *   set, because resolving a P2P service may create and/or enable an interface whose 
  * - If kDNSServiceInterfaceIndexP2P is passed to DNSServiceResolve, it is
  *   mapped internally to kDNSServiceInterfaceIndexAny with the kDNSServiceFlagsIncludeP2P
  *   set, because resolving a P2P service may create and/or enable an interface whose 
@@ -705,6 +825,28 @@ DNSServiceErrorType DNSSD_API DNSServiceGetProperty
 #define kDNSServiceProperty_DaemonVersion "DaemonVersion"
 
 
 #define kDNSServiceProperty_DaemonVersion "DaemonVersion"
 
 
+// Map the source port of the local UDP socket that was opened for sending the DNS query
+// to the process ID of the application that triggered the DNS resolution.
+//
+/* DNSServiceGetPID() Parameters:
+ *
+ * srcport:         Source port (in network byte order) of the UDP socket that was created by
+ *                  mDNSResponder to send the DNS query on the wire.
+ *
+ * pid:             Process ID of the application that started the name resolution which triggered
+ *                  mDNSResponder to send the query on the wire. The value can be -1 if the srcport
+ *                  cannot be mapped.
+ *
+ * return value:    Returns kDNSServiceErr_NoError on success, or kDNSServiceErr_ServiceNotRunning
+ *                  if the daemon is not running. The value of the pid is undefined if the return
+ *                  value has error.
+ */
+DNSServiceErrorType DNSSD_API DNSServiceGetPID
+(
+    uint16_t srcport,
+    int32_t *pid
+);
+
 /*********************************************************************************************
 *
 * Unix Domain Socket access, DNSServiceRef deallocation, and data processing functions
 /*********************************************************************************************
 *
 * Unix Domain Socket access, DNSServiceRef deallocation, and data processing functions
@@ -777,9 +919,7 @@ DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef);
  * is invalidated when this function is called - the DNSRecordRef may not be used in subsequent
  * functions.
  *
  * is invalidated when this function is called - the DNSRecordRef may not be used in subsequent
  * functions.
  *
- * Note: This call is to be used only with the DNSServiceRef defined by this API. It is
- * not compatible with dns_service_discovery_ref objects defined in the legacy Mach-based
- * DNSServiceDiscovery.h API.
+ * Note: This call is to be used only with the DNSServiceRef defined by this API.
  *
  * sdRef:           A DNSServiceRef initialized by any of the DNSService calls.
  *
  *
  * sdRef:           A DNSServiceRef initialized by any of the DNSService calls.
  *
@@ -990,6 +1130,31 @@ typedef void (DNSSD_API *DNSServiceRegisterReply)
  *
  *                  % dns-sd -R Test '_test._tcp,s\.one,s\,two,s\\three,s\000four' local 123
  *
  *
  *                  % dns-sd -R Test '_test._tcp,s\.one,s\,two,s\\three,s\000four' local 123
  *
+ *                  When a service is registered, all the clients browsing for the registered
+ *                  type ("regtype") will discover it. If the discovery should be
+ *                  restricted to a smaller set of well known peers, the service can be
+ *                  registered with additional data (group identifier) that is known
+ *                  only to a smaller set of peers. The group identifier should follow primary
+ *                  service type using a colon (":") as a delimeter. If subtypes are also present,
+ *                  it should be given before the subtype as shown below.
+ *
+ *                  % dns-sd -R _test1 _http._tcp:mygroup1 local 1001 
+ *                  % dns-sd -R _test2 _http._tcp:mygroup2 local 1001 
+ *                  % dns-sd -R _test3 _http._tcp:mygroup3,HasFeatureA local 1001 
+ *
+ *                  Now:
+ *                  % dns-sd -B _http._tcp:"mygroup1"                # will discover only test1
+ *                  % dns-sd -B _http._tcp:"mygroup2"                # will discover only test2
+ *                  % dns-sd -B _http._tcp:"mygroup3",HasFeatureA    # will discover only test3
+ *                  
+ *                  By specifying the group information, only the members of that group are
+ *                  discovered.
+ *
+ *                  The group identifier itself is not sent in clear. Only a hash of the group
+ *                  identifier is sent and the clients discover them anonymously. The group identifier
+ *                  may be up to 256 bytes long and may contain any eight bit values except comma which
+ *                  should be escaped.
+ *
  * domain:          If non-NULL, specifies the domain on which to advertise the service.
  *                  Most applications will not specify a domain, instead automatically
  *                  registering in the default domain(s).
  * domain:          If non-NULL, specifies the domain on which to advertise the service.
  *                  Most applications will not specify a domain, instead automatically
  *                  registering in the default domain(s).
@@ -1246,7 +1411,10 @@ typedef void (DNSSD_API *DNSServiceBrowseReply)
  *                  A client may optionally specify a single subtype to perform filtered browsing:
  *                  e.g. browsing for "_primarytype._tcp,_subtype" will discover only those
  *                  instances of "_primarytype._tcp" that were registered specifying "_subtype"
  *                  A client may optionally specify a single subtype to perform filtered browsing:
  *                  e.g. browsing for "_primarytype._tcp,_subtype" will discover only those
  *                  instances of "_primarytype._tcp" that were registered specifying "_subtype"
- *                  in their list of registered subtypes.
+ *                  in their list of registered subtypes. Additionally, a group identifier may
+ *                  also be specified before the subtype e.g., _primarytype._tcp:GroupID, which
+ *                  will discover only the members that register the service with GroupID. See
+ *                  DNSServiceRegister for more details.
  *
  * domain:          If non-NULL, specifies the domain on which to browse for services.
  *                  Most applications will not specify a domain, instead browsing on the
  *
  * domain:          If non-NULL, specifies the domain on which to browse for services.
  *                  Most applications will not specify a domain, instead browsing on the
@@ -1650,7 +1818,6 @@ DNSServiceErrorType DNSSD_API DNSServiceGetAddrInfo
 
 DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef);
 
 
 DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef);
 
-
 /* DNSServiceRegisterRecord
  *
  * Register an individual resource record on a connected DNSServiceRef.
 /* DNSServiceRegisterRecord
  *
  * Register an individual resource record on a connected DNSServiceRef.
@@ -1759,8 +1926,7 @@ DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord
  *
  * Parameters:
  *
  *
  * Parameters:
  *
- * flags:           Pass kDNSServiceFlagsForce to force immediate deletion of record,
- *                  instead of after some number of reconfirmation queries have gone unanswered.
+ * flags:           Not currently used.
  *
  * interfaceIndex:  Specifies the interface of the record in question.
  *                  The caller must specify the interface.
  *
  * interfaceIndex:  Specifies the interface of the record in question.
  *                  The caller must specify the interface.
@@ -1802,8 +1968,12 @@ DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord
 /* DNSServiceNATPortMappingCreate
  *
  * Request a port mapping in the NAT gateway, which maps a port on the local machine
 /* DNSServiceNATPortMappingCreate
  *
  * Request a port mapping in the NAT gateway, which maps a port on the local machine
- * to an external port on the NAT. The NAT should support either the NAT-PMP or the UPnP IGD
- * protocol for this API to create a successful mapping.
+ * to an external port on the NAT. The NAT should support either PCP, NAT-PMP or the
+ * UPnP/IGD protocol for this API to create a successful mapping. Note that this API
+ * currently supports IPv4 addresses/mappings only. If the NAT gateway supports PCP and
+ * returns an IPv6 address (incorrectly, since this API specifically requests IPv4
+ * addresses), the DNSServiceNATPortMappingReply callback will be invoked with errorCode
+ * kDNSServiceErr_NATPortMappingUnsupported.
  *
  * The port mapping will be renewed indefinitely until the client process exits, or
  * explicitly terminates the port mapping request by calling DNSServiceRefDeallocate().
  *
  * The port mapping will be renewed indefinitely until the client process exits, or
  * explicitly terminates the port mapping request by calling DNSServiceRefDeallocate().
@@ -2433,6 +2603,34 @@ DNSServiceErrorType DNSSD_API DNSServiceSleepKeepalive
 );
 #endif
 
 );
 #endif
 
+#ifdef APPLE_OSX_mDNSResponder
+/* DNSServiceCreateDelegateConnection()
+ *
+ * Create a delegate connection to the daemon allowing efficient registration of
+ * multiple individual records.
+ *
+ * Parameters:
+ *
+ * sdRef:           A pointer to an uninitialized DNSServiceRef. Deallocating
+ *                  the reference (via DNSServiceRefDeallocate()) severs the
+ *                  connection and deregisters all records registered on this connection.
+ *
+ * pid :            Process ID of the delegate
+ *
+ * uuid:            UUID of the delegate
+ *
+ *                  Note that only one of the two arguments (pid or uuid) can be specified. If pid
+ *                  is zero, uuid will be assumed to be a valid value; otherwise pid will be used.
+ *
+ * return value:    Returns kDNSServiceErr_NoError on success, otherwise returns
+ *                  an error code indicating the specific failure that occurred (in which
+ *                  case the DNSServiceRef is not initialized). kDNSServiceErr_NotAuth is
+ *                  returned to indicate that the calling process does not have entitlements
+ *                  to use this API.
+ */
+DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef *sdRef, int32_t pid, uuid_t uuid);
+#endif
+
 #ifdef __APPLE_API_PRIVATE
 
 #define kDNSServiceCompPrivateDNS   "PrivateDNS"
 #ifdef __APPLE_API_PRIVATE
 
 #define kDNSServiceCompPrivateDNS   "PrivateDNS"
index a5ee500e40273d79d93c340986683057daaa2fd5..aa06650ac10270c2d0f18a96f70516b8757289ab 100644 (file)
@@ -3104,8 +3104,10 @@ 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; }
                      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, mDNSu32 timeout, mDNSBool cellIntf, mDNSu16 resGroupID)
-{ ( void ) m; ( void ) d; ( void ) interface; ( void ) addr; ( void ) port; ( void ) scoped; ( void ) timeout; (void) cellIntf; (void) resGroupID; return(NULL); }
+DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const int serviceID, const mDNSAddr *addr, const mDNSIPPort port, 
+                             mDNSu32 scoped, mDNSu32 timeout, mDNSBool cellIntf, mDNSu16 resGroupID, mDNSBool reqA, mDNSBool reqAAAA, mDNSBool reqDO)
+{ ( void ) m; ( void ) d; ( void ) interface; ( void ) serviceID; ( void ) addr; ( void ) port; ( void ) scoped; ( void ) timeout; (void) cellIntf; 
+ (void) resGroupID; (void) reqA; (void) reqAAAA; (void) reqDO; 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; }
 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; }
@@ -3132,6 +3134,8 @@ mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info,
 mStatus mDNS_StopQuery(mDNS *const m, DNSQuestion *const question) { ( void ) m; ( void ) question; return 0; }
 void TriggerEventCompletion(void);
 void TriggerEventCompletion() {}
 mStatus mDNS_StopQuery(mDNS *const m, DNSQuestion *const question) { ( void ) m; ( void ) question; return 0; }
 void TriggerEventCompletion(void);
 void TriggerEventCompletion() {}
+int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q);
+int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) { ( void ) rr; ( void ) q; return 1;}
 mDNS mDNSStorage;
 
 
 mDNS mDNSStorage;
 
 
index 0815b8d1514358b47dc238acfb2ee276976f48de..cb143104399b530996d4ad9846bf2cc6f76bfe21 100644 (file)
@@ -412,7 +412,7 @@ DNSServiceErrorType DNSServiceBrowse
     x->q.QuestionContext = x;
 
     // Do the operation
     x->q.QuestionContext = x;
 
     // Do the operation
-    err = mDNS_StartBrowse(&mDNSStorage, &x->q, &t, &d, mDNSInterface_Any, flags, (flags & kDNSServiceFlagsForceMulticast) != 0, (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0, FoundInstance, x);
+    err = mDNS_StartBrowse(&mDNSStorage, &x->q, &t, &d, mDNSNULL, mDNSInterface_Any, flags, (flags & kDNSServiceFlagsForceMulticast) != 0, (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0, FoundInstance, x);
     if (err) { mDNSPlatformMemFree(x); errormsg = "mDNS_StartBrowse"; goto fail; }
 
     // Succeeded: Wrap up and return
     if (err) { mDNSPlatformMemFree(x); errormsg = "mDNS_StartBrowse"; goto fail; }
 
     // Succeeded: Wrap up and return
@@ -516,10 +516,13 @@ DNSServiceErrorType DNSServiceResolve
     x->qSRV.RetryWithSearchDomains = mDNSfalse;
     x->qSRV.TimeoutQuestion     = 0;
     x->qSRV.WakeOnResolve       = 0;
     x->qSRV.RetryWithSearchDomains = mDNSfalse;
     x->qSRV.TimeoutQuestion     = 0;
     x->qSRV.WakeOnResolve       = 0;
-    x->qSRV.UseBrackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0;
+    x->qSRV.UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0;
     x->qSRV.ValidationRequired  = 0;
     x->qSRV.ValidatingResponse  = 0;
     x->qSRV.ValidationRequired  = 0;
     x->qSRV.ValidatingResponse  = 0;
+    x->qSRV.ProxyQuestion       = 0;
     x->qSRV.qnameOrig           = mDNSNULL;
     x->qSRV.qnameOrig           = mDNSNULL;
+    x->qSRV.AnonInfo            = mDNSNULL;
+    x->qSRV.pid                 = mDNSPlatformGetPID();
     x->qSRV.QuestionCallback    = FoundServiceInfo;
     x->qSRV.QuestionContext     = x;
 
     x->qSRV.QuestionCallback    = FoundServiceInfo;
     x->qSRV.QuestionContext     = x;
 
@@ -540,10 +543,13 @@ DNSServiceErrorType DNSServiceResolve
     x->qTXT.RetryWithSearchDomains = mDNSfalse;
     x->qTXT.TimeoutQuestion     = 0;
     x->qTXT.WakeOnResolve       = 0;
     x->qTXT.RetryWithSearchDomains = mDNSfalse;
     x->qTXT.TimeoutQuestion     = 0;
     x->qTXT.WakeOnResolve       = 0;
-    x->qTXT.UseBrackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0;
+    x->qTXT.UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0;
     x->qTXT.ValidationRequired  = 0;
     x->qTXT.ValidatingResponse  = 0;
     x->qTXT.ValidationRequired  = 0;
     x->qTXT.ValidatingResponse  = 0;
+    x->qTXT.ProxyQuestion       = 0;
     x->qTXT.qnameOrig           = mDNSNULL;
     x->qTXT.qnameOrig           = mDNSNULL;
+    x->qTXT.AnonInfo            = mDNSNULL;
+    x->qTXT.pid                 = mDNSPlatformGetPID();
     x->qTXT.QuestionCallback    = FoundServiceInfo;
     x->qTXT.QuestionContext     = x;
 
     x->qTXT.QuestionCallback    = FoundServiceInfo;
     x->qTXT.QuestionContext     = x;
 
@@ -672,9 +678,15 @@ DNSServiceErrorType DNSServiceQueryRecord
     x->q.SearchListIndex     = 0;
     x->q.AppendSearchDomains = 0;
     x->q.RetryWithSearchDomains = mDNSfalse;
     x->q.SearchListIndex     = 0;
     x->q.AppendSearchDomains = 0;
     x->q.RetryWithSearchDomains = mDNSfalse;
+    x->q.TimeoutQuestion     = 0;
     x->q.WakeOnResolve       = 0;
     x->q.WakeOnResolve       = 0;
-    x->q.UseBrackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0;
+    x->q.UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0;
+    x->q.ValidationRequired  = 0;
+    x->q.ValidatingResponse  = 0;
+    x->q.ProxyQuestion       = 0;
     x->q.qnameOrig           = mDNSNULL;
     x->q.qnameOrig           = mDNSNULL;
+    x->q.AnonInfo            = mDNSNULL;
+    x->q.pid                 = mDNSPlatformGetPID();
     x->q.QuestionCallback    = DNSServiceQueryRecordResponse;
     x->q.QuestionContext     = x;
 
     x->q.QuestionCallback    = DNSServiceQueryRecordResponse;
     x->q.QuestionContext     = x;
 
index f5e382d815391816cd9e48da5fec38cd71b299fa..d21658fa20fd3de50b8aa8fcabd12dd54c32a655 100644 (file)
@@ -31,6 +31,8 @@
 
 #if APPLE_OSX_mDNSResponder
 #include <mach-o/dyld.h>
 
 #if APPLE_OSX_mDNSResponder
 #include <mach-o/dyld.h>
+#include <uuid/uuid.h>
+#include <TargetConditionals.h>
 #endif
 
 #include "dnssd_ipc.h"
 #endif
 
 #include "dnssd_ipc.h"
@@ -96,7 +98,11 @@ static void syslog( int priority, const char * message, ...)
 // 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
 
 // 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
+// If the UDS client has not received a response from the daemon in 60 secs, it is unlikely to get one
+// Note: Timeout of 3 secs should be sufficient in normal scenarios, but 60 secs is chosen as a safeguard since
+// some clients may come up before mDNSResponder itself after a BOOT and on rare ocassions IOPM/Keychain/D2D calls
+// in mDNSResponder's INIT may take a much longer time to return
+#define DNSSD_CLIENT_TIMEOUT 60
 
 #ifndef CTL_PATH_PREFIX
 #define CTL_PATH_PREFIX "/var/tmp/dnssd_result_socket."
 
 #ifndef CTL_PATH_PREFIX
 #define CTL_PATH_PREFIX "/var/tmp/dnssd_result_socket."
@@ -218,7 +224,11 @@ static int read_all(dnssd_sock_t sd, char *buf, int 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
         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) && (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;
         if ((num_read == 0) || (num_read < 0) || (num_read > len))
         {
             int printWarn = 0;
@@ -277,29 +287,29 @@ static int more_bytes(dnssd_sock_t sd)
         int nfdbits = sizeof (int) * 8;
         int nints = (sd/nfdbits) + 1;
         fs = (fd_set *)calloc(nints, sizeof(int));
         int nfdbits = sizeof (int) * 8;
         int nints = (sd/nfdbits) + 1;
         fs = (fd_set *)calloc(nints, sizeof(int));
-        if (fs == NULL) { syslog(LOG_WARNING, "dnssd_clientstub more_bytes: malloc failed"); return 0; }
+        if (fs == NULL) 
+        { 
+            syslog(LOG_WARNING, "dnssd_clientstub more_bytes: malloc failed"); 
+            return 0; 
+        }
     }
     FD_SET(sd, fs);
     ret = select((int)sd+1, fs, (fd_set*)NULL, (fd_set*)NULL, &tv);
     }
     FD_SET(sd, fs);
     ret = select((int)sd+1, fs, (fd_set*)NULL, (fd_set*)NULL, &tv);
-    if (fs != &readfds) free(fs);
+    if (fs != &readfds) 
+        free(fs);
     return (ret > 0);
 }
 
     return (ret > 0);
 }
 
-// Wait for daemon to write to socket
-static int wait_for_daemon(dnssd_sock_t sock, int timeout)
+// set_waitlimit() implements a timeout using select. It is called from deliver_request() before recv() OR accept()
+// to ensure the UDS clients are not blocked in these system calls indefinitely.
+// Note: Ideally one should never be blocked here, because it indicates either mDNSResponder daemon is not yet up/hung/
+// superbusy/crashed or some other OS bug. For eg: On Windows which suffers from 3rd party software 
+// (primarily 3rd party firewall software) interfering with proper functioning of the TCP protocol stack it is possible 
+// the next operation on this socket(recv/accept) is blocked since we depend on TCP to communicate with the system service.
+static int set_waitlimit(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)
+    // To prevent stack corruption since select does not work with timeout if fds > FD_SETSIZE(1024)
+    if (!gDaemonErr && sock < FD_SETSIZE)
     {
         struct timeval tv;
         fd_set set;
     {
         struct timeval tv;
         fd_set set;
@@ -310,11 +320,11 @@ static int wait_for_daemon(dnssd_sock_t sock, int timeout)
         tv.tv_usec = 0;
         if (!select((int)(sock + 1), &set, NULL, NULL, &tv))
         {
         tv.tv_usec = 0;
         if (!select((int)(sock + 1), &set, NULL, NULL, &tv))
         {
-            syslog(LOG_WARNING, "dnssd_clientstub wait_for_daemon timed out");
+            // Ideally one should never hit this case: See comments before set_waitlimit()
+            syslog(LOG_WARNING, "dnssd_clientstub set_waitlimit:_daemon timed out (%d secs) without any response: Socket %d", timeout, sock);
             gDaemonErr = kDNSServiceErr_Timeout;
         }
     }
             gDaemonErr = kDNSServiceErr_Timeout;
         }
     }
-#endif
     return gDaemonErr;
 }
 
     return gDaemonErr;
 }
 
@@ -430,16 +440,16 @@ static void FreeDNSServiceOp(DNSServiceOp *x)
 // Return a connected service ref (deallocate with DNSServiceRefDeallocate)
 static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags flags, uint32_t op, ProcessReplyFn ProcessReply, void *AppCallback, void *AppContext)
 {
 // Return a connected service ref (deallocate with DNSServiceRefDeallocate)
 static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags flags, uint32_t op, ProcessReplyFn ProcessReply, void *AppCallback, void *AppContext)
 {
-    #if APPLE_OSX_mDNSResponder
-    int NumTries = DNSSD_CLIENT_MAXTRIES;
-    #else
     int NumTries = 0;
     int NumTries = 0;
-    #endif
 
     dnssd_sockaddr_t saddr;
     DNSServiceOp *sdr;
 
 
     dnssd_sockaddr_t saddr;
     DNSServiceOp *sdr;
 
-    if (!ref) { syslog(LOG_WARNING, "dnssd_clientstub DNSService operation with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; }
+    if (!ref) 
+    { 
+        syslog(LOG_WARNING, "dnssd_clientstub DNSService operation with NULL DNSServiceRef"); 
+        return kDNSServiceErr_BadParam; 
+    }
 
     if (flags & kDNSServiceFlagsShareConnection)
     {
 
     if (flags & kDNSServiceFlagsShareConnection)
     {
@@ -448,10 +458,10 @@ static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags f
             syslog(LOG_WARNING, "dnssd_clientstub kDNSServiceFlagsShareConnection used with NULL DNSServiceRef");
             return kDNSServiceErr_BadParam;
         }
             syslog(LOG_WARNING, "dnssd_clientstub kDNSServiceFlagsShareConnection used with NULL DNSServiceRef");
             return kDNSServiceErr_BadParam;
         }
-        if (!DNSServiceRefValid(*ref) || (*ref)->op != connection_request || (*ref)->primary)
+        if (!DNSServiceRefValid(*ref) || ((*ref)->op != connection_request && (*ref)->op != connection_delegate_request) || (*ref)->primary)
         {
         {
-            syslog(LOG_WARNING, "dnssd_clientstub kDNSServiceFlagsShareConnection used with invalid DNSServiceRef %p %08X %08X",
-                   (*ref), (*ref)->sockfd, (*ref)->validator);
+            syslog(LOG_WARNING, "dnssd_clientstub kDNSServiceFlagsShareConnection used with invalid DNSServiceRef %p %08X %08X op %d",
+                   (*ref), (*ref)->sockfd, (*ref)->validator, (*ref)->op);
             *ref = NULL;
             return kDNSServiceErr_BadReference;
         }
             *ref = NULL;
             return kDNSServiceErr_BadReference;
         }
@@ -465,11 +475,17 @@ static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags f
         if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) { *ref = NULL; return kDNSServiceErr_ServiceNotRunning; }
     }
     // <rdar://problem/4096913> If the system service is disabled, we only want to try to connect once
         if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) { *ref = NULL; return kDNSServiceErr_ServiceNotRunning; }
     }
     // <rdar://problem/4096913> If the system service is disabled, we only want to try to connect once
-    if (IsSystemServiceDisabled()) NumTries = DNSSD_CLIENT_MAXTRIES;
+    if (IsSystemServiceDisabled()) 
+        NumTries = DNSSD_CLIENT_MAXTRIES;
     #endif
 
     sdr = malloc(sizeof(DNSServiceOp));
     #endif
 
     sdr = malloc(sizeof(DNSServiceOp));
-    if (!sdr) { syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: malloc failed"); *ref = NULL; return kDNSServiceErr_NoMemory; }
+    if (!sdr) 
+    { 
+        syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: malloc failed"); 
+        *ref = NULL; 
+        return kDNSServiceErr_NoMemory; 
+    }
     sdr->next          = NULL;
     sdr->primary       = NULL;
     sdr->sockfd        = dnssd_InvalidSocket;
     sdr->next          = NULL;
     sdr->primary       = NULL;
     sdr->sockfd        = dnssd_InvalidSocket;
@@ -493,10 +509,12 @@ static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags f
     if (flags & kDNSServiceFlagsShareConnection)
     {
         DNSServiceOp **p = &(*ref)->next;       // Append ourselves to end of primary's list
     if (flags & kDNSServiceFlagsShareConnection)
     {
         DNSServiceOp **p = &(*ref)->next;       // Append ourselves to end of primary's list
-        while (*p) p = &(*p)->next;
+        while (*p) 
+            p = &(*p)->next;
         *p = sdr;
         // Preincrement counter before we use it -- it helps with debugging if we know the all-zeroes ID should never appear
         *p = sdr;
         // Preincrement counter before we use it -- it helps with debugging if we know the all-zeroes ID should never appear
-        if (++(*ref)->uid.u32[0] == 0) ++(*ref)->uid.u32[1];    // In parent DNSServiceOp increment UID counter
+        if (++(*ref)->uid.u32[0] == 0) 
+            ++(*ref)->uid.u32[1];               // In parent DNSServiceOp increment UID counter
         sdr->primary    = *ref;                 // Set our primary pointer
         sdr->sockfd     = (*ref)->sockfd;       // Inherit primary's socket
         sdr->validator  = (*ref)->validator;
         sdr->primary    = *ref;                 // Set our primary pointer
         sdr->sockfd     = (*ref)->sockfd;       // Inherit primary's socket
         sdr->validator  = (*ref)->validator;
@@ -537,18 +555,30 @@ static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags f
         }
         #endif
         #endif
         }
         #endif
         #endif
-
+        
         while (1)
         {
             int err = connect(sdr->sockfd, (struct sockaddr *) &saddr, sizeof(saddr));
         while (1)
         {
             int err = connect(sdr->sockfd, (struct sockaddr *) &saddr, sizeof(saddr));
-            if (!err) break; // If we succeeded, return sdr
+            if (!err)
+                break; // If we succeeded, return sdr
             // If we failed, then it may be because the daemon is still launching.
             // This can happen for processes that launch early in the boot process, while the
             // If we failed, then it may be because the daemon is still launching.
             // This can happen for processes that launch early in the boot process, while the
-            // daemon is still coming up. Rather than fail here, we'll wait a bit and try again.
-            // If, after four seconds, we still can't connect to the daemon,
+            // daemon is still coming up. Rather than fail here, we wait 1 sec and try again.
+            // If, after DNSSD_CLIENT_MAXTRIES, we still can't connect to the daemon,
             // then we give up and return a failure code.
             // then we give up and return a failure code.
-            if (++NumTries < DNSSD_CLIENT_MAXTRIES) sleep(1); // Sleep a bit, then try again
-            else { dnssd_close(sdr->sockfd); FreeDNSServiceOp(sdr); return kDNSServiceErr_ServiceNotRunning; }
+            if (++NumTries < DNSSD_CLIENT_MAXTRIES)
+            {
+                syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: connect()-> No of tries: %d", NumTries);  
+                sleep(1); // Sleep a bit, then try again
+            }
+            else 
+            {
+                syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: connect() failed Socket:%d Err:%d Errno:%d %s", 
+                        sdr->sockfd, err, dnssd_errno, dnssd_strerror(dnssd_errno));
+                dnssd_close(sdr->sockfd); 
+                FreeDNSServiceOp(sdr); 
+                return kDNSServiceErr_ServiceNotRunning; 
+            }
         }
         //printf("ConnectToServer opened socket %d\n", sdr->sockfd);
     }
         }
         //printf("ConnectToServer opened socket %d\n", sdr->sockfd);
     }
@@ -580,11 +610,17 @@ static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr)
 
     if (!DNSServiceRefValid(sdr))
     {
 
     if (!DNSServiceRefValid(sdr))
     {
+        if (hdr)
+            free(hdr);
         syslog(LOG_WARNING, "dnssd_clientstub deliver_request: invalid DNSServiceRef %p %08X %08X", sdr, sdr->sockfd, sdr->validator);
         return kDNSServiceErr_BadReference;
     }
 
         syslog(LOG_WARNING, "dnssd_clientstub deliver_request: invalid DNSServiceRef %p %08X %08X", sdr, sdr->sockfd, sdr->validator);
         return kDNSServiceErr_BadReference;
     }
 
-    if (!hdr) { syslog(LOG_WARNING, "dnssd_clientstub deliver_request: !hdr"); return kDNSServiceErr_Unknown; }
+    if (!hdr) 
+    { 
+        syslog(LOG_WARNING, "dnssd_clientstub deliver_request: !hdr"); 
+        return kDNSServiceErr_Unknown;  
+    }
 
     if (MakeSeparateReturnSocket)
     {
 
     if (MakeSeparateReturnSocket)
     {
@@ -654,7 +690,8 @@ static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr)
     // any associated data does not work reliably -- e.g. one particular issue we ran
     // into is that if the receiving program is in a kqueue loop waiting to be notified
     // of the received message, it doesn't get woken up when the control message arrives.
     // any associated data does not work reliably -- e.g. one particular issue we ran
     // into is that if the receiving program is in a kqueue loop waiting to be notified
     // of the received message, it doesn't get woken up when the control message arrives.
-    if (MakeSeparateReturnSocket || sdr->op == send_bpf) datalen--;     // Okay to use sdr->op when checking for op == send_bpf
+    if (MakeSeparateReturnSocket || sdr->op == send_bpf) 
+        datalen--;     // Okay to use sdr->op when checking for op == send_bpf
 #endif
 
     // At this point, our listening socket is set up and waiting, if necessary, for the daemon to connect back to
 #endif
 
     // At this point, our listening socket is set up and waiting, if necessary, for the daemon to connect back to
@@ -682,51 +719,58 @@ static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr)
     }
 #endif
 
     }
 #endif
 
-    if (!MakeSeparateReturnSocket) errsd = sdr->sockfd;
+    if (!MakeSeparateReturnSocket) 
+        errsd = sdr->sockfd;
     if (MakeSeparateReturnSocket || sdr->op == send_bpf)    // Okay to use sdr->op when checking for op == send_bpf
     {
 #if defined(USE_TCP_LOOPBACK) || defined(USE_NAMED_ERROR_RETURN_SOCKET)
     if (MakeSeparateReturnSocket || sdr->op == send_bpf)    // Okay to use sdr->op when checking for op == send_bpf
     {
 #if defined(USE_TCP_LOOPBACK) || defined(USE_NAMED_ERROR_RETURN_SOCKET)
-        // At this point we may block in accept for a few milliseconds waiting for the daemon to connect back to us,
-        // but that's okay -- the daemon is a trusted service and we know if won't take more than a few milliseconds to respond.
+        // At this point we may wait in accept for a few milliseconds waiting for the daemon to connect back to us,
+        // but that's okay -- the daemon should not take more than a few milliseconds to respond.
+        // set_waitlimit() ensures we do not block indefinitely just in case something is wrong
         dnssd_sockaddr_t daddr;
         dnssd_socklen_t len = sizeof(daddr);
         dnssd_sockaddr_t daddr;
         dnssd_socklen_t len = sizeof(daddr);
-        if ((err = wait_for_daemon(listenfd, DNSSD_CLIENT_TIMEOUT)) != kDNSServiceErr_NoError) goto cleanup;
+        if ((err = set_waitlimit(listenfd, DNSSD_CLIENT_TIMEOUT)) != kDNSServiceErr_NoError) 
+            goto cleanup;
         errsd = accept(listenfd, (struct sockaddr *)&daddr, &len);
         errsd = accept(listenfd, (struct sockaddr *)&daddr, &len);
-        if (!dnssd_SocketValid(errsd)) deliver_request_bailout("accept");
+        if (!dnssd_SocketValid(errsd)) 
+            deliver_request_bailout("accept");
 #else
 
         struct iovec vec = { ((char *)hdr) + sizeof(ipc_msg_hdr) + datalen, 1 }; // Send the last byte along with the SCM_RIGHTS
         struct msghdr msg;
         struct cmsghdr *cmsg;
 #else
 
         struct iovec vec = { ((char *)hdr) + sizeof(ipc_msg_hdr) + datalen, 1 }; // Send the last byte along with the SCM_RIGHTS
         struct msghdr msg;
         struct cmsghdr *cmsg;
-        char cbuf[CMSG_SPACE(sizeof(dnssd_sock_t))];
-
-        if (sdr->op == send_bpf)    // Okay to use sdr->op when checking for op == send_bpf
-        {
-            int i;
-            char p[12];     // Room for "/dev/bpf999" with terminating null
-            for (i=0; i<100; i++)
-            {
-                snprintf(p, sizeof(p), "/dev/bpf%d", i);
-                listenfd = open(p, O_RDWR, 0);
-                //if (dnssd_SocketValid(listenfd)) syslog(LOG_WARNING, "Sending fd %d for %s", listenfd, p);
-                if (!dnssd_SocketValid(listenfd) && dnssd_errno != EBUSY)
-                    syslog(LOG_WARNING, "Error opening %s %d (%s)", p, dnssd_errno, dnssd_strerror(dnssd_errno));
-                if (dnssd_SocketValid(listenfd) || dnssd_errno != EBUSY) break;
-            }
-        }
+        char cbuf[CMSG_SPACE(4 * sizeof(dnssd_sock_t))];
 
         msg.msg_name       = 0;
         msg.msg_namelen    = 0;
         msg.msg_iov        = &vec;
         msg.msg_iovlen     = 1;
 
         msg.msg_name       = 0;
         msg.msg_namelen    = 0;
         msg.msg_iov        = &vec;
         msg.msg_iovlen     = 1;
-        msg.msg_control    = cbuf;
-        msg.msg_controllen = CMSG_LEN(sizeof(dnssd_sock_t));
         msg.msg_flags      = 0;
         msg.msg_flags      = 0;
-        cmsg = CMSG_FIRSTHDR(&msg);
-        cmsg->cmsg_len     = CMSG_LEN(sizeof(dnssd_sock_t));
-        cmsg->cmsg_level   = SOL_SOCKET;
-        cmsg->cmsg_type    = SCM_RIGHTS;
-        *((dnssd_sock_t *)CMSG_DATA(cmsg)) = listenfd;
+        if (MakeSeparateReturnSocket || sdr->op == send_bpf)    // Okay to use sdr->op when checking for op == send_bpf
+        {
+            if (sdr->op == send_bpf)
+            {
+                int i;
+                char p[12];     // Room for "/dev/bpf999" with terminating null
+                for (i=0; i<100; i++)
+                {
+                    snprintf(p, sizeof(p), "/dev/bpf%d", i);
+                    listenfd = open(p, O_RDWR, 0);
+                    //if (dnssd_SocketValid(listenfd)) syslog(LOG_WARNING, "Sending fd %d for %s", listenfd, p);
+                    if (!dnssd_SocketValid(listenfd) && dnssd_errno != EBUSY)
+                        syslog(LOG_WARNING, "Error opening %s %d (%s)", p, dnssd_errno, dnssd_strerror(dnssd_errno));
+                    if (dnssd_SocketValid(listenfd) || dnssd_errno != EBUSY) break;
+                }
+            }
+            msg.msg_control    = cbuf;
+            msg.msg_controllen = CMSG_LEN(sizeof(dnssd_sock_t));
+
+            cmsg = CMSG_FIRSTHDR(&msg);
+            cmsg->cmsg_len     = CMSG_LEN(sizeof(dnssd_sock_t));
+            cmsg->cmsg_level   = SOL_SOCKET;
+            cmsg->cmsg_type    = SCM_RIGHTS;
+            *((dnssd_sock_t *)CMSG_DATA(cmsg)) = listenfd;
+        }
 
 #if TEST_KQUEUE_CONTROL_MESSAGE_BUG
         sleep(1);
 
 #if TEST_KQUEUE_CONTROL_MESSAGE_BUG
         sleep(1);
@@ -753,25 +797,27 @@ static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr)
 #endif // DEBUG_64BIT_SCM_RIGHTS
 
 #endif
 #endif // DEBUG_64BIT_SCM_RIGHTS
 
 #endif
-        // Close our end of the socketpair *before* blocking in read_all to get the four-byte error code.
-        // Otherwise, if the daemon closes our socket (or crashes), we block in read_all() forever
-        // because the socket is not closed (we still have an open reference to it ourselves).
+        // Close our end of the socketpair *before* calling read_all() to get the four-byte error code.
+        // Otherwise, if the daemon closes our socket (or crashes), we will have to wait for a timeout
+        // in read_all() because the socket is not closed (we still have an open reference to it)
+        // Note: listenfd is overwritten in the case of send_bpf above and that will be closed here
+        // for send_bpf operation.
         dnssd_close(listenfd);
         dnssd_close(listenfd);
-        listenfd = dnssd_InvalidSocket;     // Make sure we don't close it a second time in the cleanup handling below
+        listenfd = dnssd_InvalidSocket; // Make sure we don't close it a second time in the cleanup handling below
     }
 
     }
 
-    // At this point we may block in read_all for a few milliseconds waiting for the daemon to send us the error code,
-    // but that's okay -- the daemon is a trusted service and we know if won't take more than a few milliseconds to respond.
+    // At this point we may wait in read_all for a few milliseconds waiting for the daemon to send us the error code,
+    // but that's okay -- the daemon should not take more than a few milliseconds to respond.
+    // set_waitlimit() ensures we do not block indefinitely just in case something is wrong
     if (sdr->op == send_bpf)    // Okay to use sdr->op when checking for op == send_bpf
         err = kDNSServiceErr_NoError;
     if (sdr->op == send_bpf)    // Okay to use sdr->op when checking for op == send_bpf
         err = kDNSServiceErr_NoError;
-    else if ((err = wait_for_daemon(errsd, DNSSD_CLIENT_TIMEOUT)) == kDNSServiceErr_NoError)
+    else if ((err = set_waitlimit(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);
     }
     {
         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);
 
 cleanup:
     //syslog(LOG_WARNING, "dnssd_clientstub deliver_request: retrieved error code %d", err);
 
 cleanup:
@@ -847,6 +893,7 @@ static void CallbackWithError(DNSServiceRef sdRef, DNSServiceErrorType error)
             if (sdr->AppCallback) ((DNSServiceDomainEnumReply) sdr->AppCallback)(sdr, 0, 0, error, NULL,                   sdr->AppContext);
             break;
         case connection_request:
             if (sdr->AppCallback) ((DNSServiceDomainEnumReply) sdr->AppCallback)(sdr, 0, 0, error, NULL,                   sdr->AppContext);
             break;
         case connection_request:
+        case connection_delegate_request:
             // This means Register Record, walk the list of DNSRecords to do the callback
             rec = sdr->rec;
             while (rec)
             // This means Register Record, walk the list of DNSRecords to do the callback
             rec = sdr->rec;
             while (rec)
@@ -1121,6 +1168,37 @@ DNSServiceErrorType DNSSD_API DNSServiceGetProperty(const char *property, void *
     return kDNSServiceErr_NoError;
 }
 
     return kDNSServiceErr_NoError;
 }
 
+DNSServiceErrorType DNSSD_API DNSServiceGetPID(const uint16_t srcport, int32_t *pid)
+{
+    char *ptr;
+    ipc_msg_hdr *hdr;
+    DNSServiceOp *tmp;
+    size_t len = sizeof(int32_t);
+
+    DNSServiceErrorType err = ConnectToServer(&tmp, 0, getpid_request, NULL, NULL, NULL);
+    if (err)
+        return err;
+
+    hdr = create_hdr(getpid_request, &len, &ptr, 0, tmp);
+    if (!hdr)
+    {
+        DNSServiceRefDeallocate(tmp);
+        return kDNSServiceErr_NoMemory;
+    }
+
+    put_uint16(srcport, &ptr);
+    err = deliver_request(hdr, tmp);        // Will free hdr for us
+
+    if (read_all(tmp->sockfd, (char*)pid, sizeof(int32_t)) < 0)
+    {
+        DNSServiceRefDeallocate(tmp);
+        return kDNSServiceErr_ServiceNotRunning;
+    }
+
+    DNSServiceRefDeallocate(tmp);
+    return kDNSServiceErr_NoError;
+}
+
 static void handle_resolve_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *end)
 {
     char fullname[kDNSServiceMaxDomainName];
 static void handle_resolve_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *end)
 {
     char fullname[kDNSServiceMaxDomainName];
@@ -1344,7 +1422,16 @@ static void handle_addrinfo_response(DNSServiceOp *const sdr, const CallbackHead
                 if (IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr)) sa6.sin6_scope_id = cbh->cb_interface;
             }
         }
                 if (IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr)) sa6.sin6_scope_id = cbh->cb_interface;
             }
         }
-        ((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, hostname, sa, ttl, sdr->AppContext);
+        // Validation results are always delivered separately from the actual results of the
+        // DNSServiceGetAddrInfo. Set the "addr" to NULL as per the documentation.
+        //
+        // Note: If we deliver validation results along with the "addr" in the future, we need
+        // a way to differentiate the negative response from validation-only response as both
+        // has zero address.
+        if (!(cbh->cb_flags & kDNSServiceFlagsValidate))
+            ((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, hostname, sa, ttl, sdr->AppContext);
+        else
+            ((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, hostname, NULL, 0, sdr->AppContext);
         // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
     }
 }
         // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
     }
 }
@@ -1368,7 +1455,10 @@ DNSServiceErrorType DNSSD_API DNSServiceGetAddrInfo
     if (!hostname) return kDNSServiceErr_BadParam;
 
     err = ConnectToServer(sdRef, flags, addrinfo_request, handle_addrinfo_response, callBack, context);
     if (!hostname) return kDNSServiceErr_BadParam;
 
     err = ConnectToServer(sdRef, flags, addrinfo_request, handle_addrinfo_response, callBack, context);
-    if (err) return err;    // On error ConnectToServer leaves *sdRef set to NULL
+    if (err)
+    {
+         return err;    // On error ConnectToServer leaves *sdRef set to NULL
+    }
 
     // Calculate total message length
     len = sizeof(flags);
 
     // Calculate total message length
     len = sizeof(flags);
@@ -1625,7 +1715,7 @@ static void ConnectionResponse(DNSServiceOp *const sdr, const CallbackHeader *co
             return;
         }
 
             return;
         }
 
-        if (sdr->op == connection_request)
+        if (sdr->op == connection_request || sdr->op == connection_delegate_request)
         {
             rec->AppCallback(rec->sdr, rec, cbh->cb_flags, cbh->cb_err, rec->AppContext);
         }
         {
             rec->AppCallback(rec->sdr, rec, cbh->cb_flags, cbh->cb_err, rec->AppContext);
         }
@@ -1654,6 +1744,72 @@ DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef)
     return err;
 }
 
     return err;
 }
 
+#if APPLE_OSX_mDNSResponder && !TARGET_IPHONE_SIMULATOR
+DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef *sdRef, int32_t pid, uuid_t uuid)
+{
+    char *ptr;
+    size_t len = 0;
+    ipc_msg_hdr *hdr;
+
+    DNSServiceErrorType err = ConnectToServer(sdRef, 0, connection_delegate_request, ConnectionResponse, NULL, NULL);
+    if (err)
+    {
+         return err;    // On error ConnectToServer leaves *sdRef set to NULL
+    }
+
+    // Only one of the two options can be set. If pid is zero, uuid is used. 
+    // If both are specified only pid will be used. We send across the pid
+    // so that the daemon knows what to read from the socket.
+
+    len += sizeof(int32_t);
+
+    hdr = create_hdr(connection_delegate_request, &len, &ptr, 0, *sdRef);
+    if (!hdr)
+    {
+        DNSServiceRefDeallocate(*sdRef);
+        *sdRef = NULL;
+        return kDNSServiceErr_NoMemory;
+    }
+
+    if (pid && setsockopt((*sdRef)->sockfd, SOL_SOCKET, SO_DELEGATED, &pid, sizeof(pid)) == -1)
+    {  
+        // Free the hdr in case we return before calling deliver_request() 
+        if (hdr)
+            free(hdr);
+        DNSServiceRefDeallocate(*sdRef);
+        *sdRef = NULL;
+        return kDNSServiceErr_NoAuth;
+    }
+
+    if (!pid && setsockopt((*sdRef)->sockfd, SOL_SOCKET, SO_DELEGATED_UUID, uuid, sizeof(uuid_t)) == -1)
+    {
+        // Free the hdr in case we return before calling deliver_request()
+        if (hdr)
+            free(hdr);
+        DNSServiceRefDeallocate(*sdRef);
+        *sdRef = NULL;
+        return kDNSServiceErr_NoAuth;
+    }
+
+    put_uint32(pid, &ptr);
+
+    err = deliver_request(hdr, *sdRef);     // Will free hdr for us
+    if (err)
+    {
+        DNSServiceRefDeallocate(*sdRef);
+        *sdRef = NULL;
+    }
+    return err;
+}
+#elif TARGET_IPHONE_SIMULATOR // This hack is for Simulator platform only
+DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef *sdRef, int32_t pid, uuid_t uuid)
+{
+    (void) pid;
+    (void) uuid;
+    return DNSServiceCreateConnection(sdRef);
+}
+#endif
+
 DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord
 (
     DNSServiceRef sdRef,
 DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord
 (
     DNSServiceRef sdRef,
@@ -1690,7 +1846,7 @@ DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord
         return kDNSServiceErr_BadReference;
     }
 
         return kDNSServiceErr_BadReference;
     }
 
-    if (sdRef->op != connection_request)
+    if (sdRef->op != connection_request && sdRef->op != connection_delegate_request)
     {
         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRegisterRecord called with non-DNSServiceCreateConnection DNSServiceRef %p %d", sdRef, sdRef->op);
         return kDNSServiceErr_BadReference;
     {
         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRegisterRecord called with non-DNSServiceCreateConnection DNSServiceRef %p %d", sdRef, sdRef->op);
         return kDNSServiceErr_BadReference;
index bb3b0df257ba05acbd211d80041a841ae1eac04c..360d703f03350af7aa5f5114f4d50f83cc50d594 100644 (file)
@@ -135,7 +135,9 @@ typedef enum
     port_mapping_request,   // New in Leopard and B4W 2.0
     addrinfo_request,
     send_bpf,               // New in SL
     port_mapping_request,   // New in Leopard and B4W 2.0
     addrinfo_request,
     send_bpf,               // New in SL
+    getpid_request,
     release_request,
     release_request,
+    connection_delegate_request,
 
     cancel_request = 63
 } request_op_t;
 
     cancel_request = 63
 } request_op_t;
index 2af79e3ca1a6f490242c671a5766469d0ff95cfb..cb4da6ecefcdda61dfc7dfa43a5cb084397559bd 100644 (file)
 
 #include "mDNSEmbeddedAPI.h"
 
 
 #include "mDNSEmbeddedAPI.h"
 
-mDNSexport int mDNS_LoggingEnabled = 0;
+mDNSexport int mDNS_LoggingEnabled       = 0;
 mDNSexport int mDNS_PacketLoggingEnabled = 0;
 mDNSexport int mDNS_PacketLoggingEnabled = 0;
+mDNSexport int mDNS_McastLoggingEnabled  = 0;
+mDNSexport int mDNS_McastTracingEnabled  = 0; 
 
 #if MDNS_DEBUGMSGS
 mDNSexport int mDNS_DebugMode = mDNStrue;
 
 #if MDNS_DEBUGMSGS
 mDNSexport int mDNS_DebugMode = mDNStrue;
index 22b4159f91897712f777b27778b843c21ca4a88e..e9b3a2793c7844e3f38a99273d98c6c4b06f5d48 100644 (file)
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 4 -*-
  *
 /* -*- Mode: C; tab-width: 4 -*-
  *
- * Copyright (c) 2003-2012 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2003-2013 Apple Computer, 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.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -54,6 +54,8 @@ mDNSBool AlwaysAppendSearchDomains = mDNSfalse;
 #include <sys/proc_info.h>  // for struct proc_bsdshortinfo
 #include <libproc.h>        // for proc_pidinfo()
 #endif //LOCAL_PEERPID
 #include <sys/proc_info.h>  // for struct proc_bsdshortinfo
 #include <libproc.h>        // for proc_pidinfo()
 #endif //LOCAL_PEERPID
+//upto 16 characters of process name (defined in <sys/proc.h> but we do not want to include that file)
+#define MAXCOMLEN 16
 
 #if APPLE_OSX_mDNSResponder
 #include <WebFilterDNS/WebFilterDNS.h>
 
 #if APPLE_OSX_mDNSResponder
 #include <WebFilterDNS/WebFilterDNS.h>
@@ -76,6 +78,8 @@ int WCFNameResolvesToName(WCFConnection *conn, char* fromName, char* toName, uid
 // User IDs for real user accounts start at 501 and count up from there
 #define SystemUID(X) ((X) <= 500)
 
 // User IDs for real user accounts start at 501 and count up from there
 #define SystemUID(X) ((X) <= 500)
 
+#define MAX_ANONYMOUS_DATA      256
+
 // ***************************************************************************
 #if COMPILER_LIKES_PRAGMA_MARK
 #pragma mark -
 // ***************************************************************************
 #if COMPILER_LIKES_PRAGMA_MARK
 #pragma mark -
@@ -130,12 +134,21 @@ typedef struct browser_t
     DNSQuestion q;
 } browser_t;
 
     DNSQuestion q;
 } browser_t;
 
+#ifdef _WIN32
+    typedef unsigned int pid_t;
+    typedef unsigned int socklen_t;
+#endif
+
 struct request_state
 {
     request_state *next;
     request_state *primary;         // If this operation is on a shared socket, pointer to primary
                                     // request_state for the original DNSServiceCreateConnection() operation
     dnssd_sock_t sd;
 struct request_state
 {
     request_state *next;
     request_state *primary;         // If this operation is on a shared socket, pointer to primary
                                     // request_state for the original DNSServiceCreateConnection() operation
     dnssd_sock_t sd;
+    pid_t process_id;               // Client's PID value
+    char  pid_name[MAXCOMLEN];      // Client's process name
+    char uuid[UUID_SIZE];
+    mDNSBool validUUID;
     dnssd_sock_t errsd;
     mDNSu32 uid;
     void * platform_data;
     dnssd_sock_t errsd;
     mDNSu32 uid;
     void * platform_data;
@@ -169,6 +182,7 @@ struct request_state
             mDNSBool ForceMCast;
             domainname regtype;
             browser_t *browsers;
             mDNSBool ForceMCast;
             domainname regtype;
             browser_t *browsers;
+            const mDNSu8 *AnonData;
         } browser;
         struct
         {
         } browser;
         struct
         {
@@ -185,6 +199,7 @@ struct request_state
             mDNSBool autorename;            // Set if this client wants us to automatically rename on conflict
             mDNSBool allowremotequery;      // Respond to unicast queries from outside the local link?
             int num_subtypes;
             mDNSBool autorename;            // Set if this client wants us to automatically rename on conflict
             mDNSBool allowremotequery;      // Respond to unicast queries from outside the local link?
             int num_subtypes;
+            mDNSBool AnonData;
             service_instance *instances;
         } servicereg;
         struct
             service_instance *instances;
         } servicereg;
         struct
@@ -193,9 +208,11 @@ struct request_state
             mDNSu32 flags;
             mDNSu32 protocol;
             DNSQuestion q4;
             mDNSu32 flags;
             mDNSu32 protocol;
             DNSQuestion q4;
-            DNSQuestion          *q42;
+            DNSQuestion *q42;
             DNSQuestion q6;
             DNSQuestion q6;
-            DNSQuestion          *q62;
+            DNSQuestion *q62;
+            mDNSu8 v4ans;
+            mDNSu8 v6ans;
         } addrinfo;
         struct
         {
         } addrinfo;
         struct
         {
@@ -204,9 +221,7 @@ struct request_state
         } pm;
         struct
         {
         } pm;
         struct
         {
-#if 0
             DNSServiceFlags flags;
             DNSServiceFlags flags;
-#endif
             DNSQuestion q_all;
             DNSQuestion q_default;
         } enumeration;
             DNSQuestion q_all;
             DNSQuestion q_default;
         } enumeration;
@@ -214,6 +229,7 @@ struct request_state
         {
             DNSQuestion q;
             DNSQuestion *q2;
         {
             DNSQuestion q;
             DNSQuestion *q2;
+            mDNSu8 ans;
         } queryrecord;
         struct
         {
         } queryrecord;
         struct
         {
@@ -259,9 +275,13 @@ static request_state *all_requests = NULL;
 #ifdef LOCAL_PEERPID
 struct proc_bsdshortinfo proc;
 #endif //LOCAL_PEERPID
 #ifdef LOCAL_PEERPID
 struct proc_bsdshortinfo proc;
 #endif //LOCAL_PEERPID
-//upto 16 characters of process name (defined in <sys/proc.h> but we do not want to include that file)
-#define MAXCOMLEN 16
-char pid_name[MAXCOMLEN];
+mDNSlocal void set_peer_pid(request_state *request);
+mDNSlocal void LogMcastClientInfo(request_state *req);
+mDNSlocal void GetMcastClients(request_state *req);
+static mDNSu32 mcount;     // tracks the current active mcast operations for McastLogging
+static mDNSu32 i_mcount;   // sets mcount when McastLogging is enabled(PROF signal is sent)
+static mDNSu32 n_mrecords; // tracks the current active mcast records for McastLogging
+static mDNSu32 n_mquests;  // tracks the current active mcast questions for McastLogging
 
 // Note asymmetry here between registration and browsing.
 // For service registrations we only automatically register in domains that explicitly appear in local configuration data
 
 // Note asymmetry here between registration and browsing.
 // For service registrations we only automatically register in domains that explicitly appear in local configuration data
@@ -287,6 +307,8 @@ mDNSexport DNameListElem *AutoBrowseDomains;        // List created from those l
 #define PID_FILE "/var/run/mDNSResponder.pid"
 #endif
 
 #define PID_FILE "/var/run/mDNSResponder.pid"
 #endif
 
+mDNSlocal char *AnonDataToString(const mDNSu8 *ad, int adlen, char *adstr, int adstrlen);
+
 // ***************************************************************************
 #if COMPILER_LIKES_PRAGMA_MARK
 #pragma mark -
 // ***************************************************************************
 #if COMPILER_LIKES_PRAGMA_MARK
 #pragma mark -
@@ -315,7 +337,7 @@ mDNSlocal void my_perror(char *errmsg)
     LogMsg("%s: %d (%s)", errmsg, dnssd_errno, dnssd_strerror(dnssd_errno));
 }
 
     LogMsg("%s: %d (%s)", errmsg, dnssd_errno, dnssd_strerror(dnssd_errno));
 }
 
-//Throttled version of my_perror: Logs once every 250 msgs
+// Throttled version of my_perror: Logs once every 250 msgs
 mDNSlocal void my_throttled_perror(char *err_msg)
 {
     static int uds_throttle_count = 0;
 mDNSlocal void my_throttled_perror(char *err_msg)
 {
     static int uds_throttle_count = 0;
@@ -323,6 +345,128 @@ mDNSlocal void my_throttled_perror(char *err_msg)
         my_perror(err_msg);
 } 
 
         my_perror(err_msg);
 } 
 
+// LogMcastQuestion/LogMcastQ should be called after the DNSQuestion struct is initialized(especially for q->TargetQID)
+// Hence all calls are made after mDNS_StartQuery()/mDNS_StopQuery()/mDNS_StopBrowse() is called.
+mDNSlocal void LogMcastQuestion(mDNS *const m, const DNSQuestion *const q, request_state *req, q_state status)
+{
+    if (mDNSOpaque16IsZero(q->TargetQID)) // Check for Mcast Query
+    {
+        mDNSBool mflag = mDNSfalse;
+        if (status == q_start)
+        {
+            if (++mcount == 1)
+                mflag = mDNStrue;
+        }
+        else
+        {
+            mcount--;
+        }
+        LogMcast("%s: %##s  (%s) (%s)  Client(%d)[%s]", status ? "+Question" : "-Question", q->qname.c, DNSTypeName(q->qtype), 
+                 q->InterfaceID == mDNSInterface_LocalOnly ? "lo" : q->InterfaceID == mDNSInterface_P2P ? "p2p" : 
+                 q->InterfaceID == mDNSInterface_Any ? "any" : InterfaceNameForID(m, q->InterfaceID), 
+                 req->process_id, req->pid_name);
+        LogMcastStateInfo(m, mflag, mDNSfalse, mDNSfalse);
+    }
+    return;
+}
+
+// LogMcastService/LogMcastS should be called after the AuthRecord struct is initialized
+// Hence all calls are made after mDNS_Register()/ just before mDNS_Deregister()
+mDNSlocal void LogMcastService(mDNS *const m, const AuthRecord *const ar, request_state *req, reg_state status)
+{
+    if (!AuthRecord_uDNS(ar)) // Check for Mcast Service
+    {
+        mDNSBool mflag = mDNSfalse;
+        if (status == reg_start)
+        {
+            if (++mcount == 1)
+                mflag = mDNStrue;
+        }
+        else
+        {
+            mcount--;
+        }
+        LogMcast("%s: %##s  (%s)  (%s)  Client(%d)[%s]", status ? "+Service" : "-Service", ar->resrec.name->c, DNSTypeName(ar->resrec.rrtype),
+                 ar->resrec.InterfaceID == mDNSInterface_LocalOnly ? "lo" : ar->resrec.InterfaceID == mDNSInterface_P2P ? "p2p" : 
+                 ar->resrec.InterfaceID == mDNSInterface_Any ? "all" : InterfaceNameForID(m, ar->resrec.InterfaceID), 
+                 req->process_id, req->pid_name);
+        LogMcastStateInfo(m, mflag, mDNSfalse, mDNSfalse);
+    }
+    return;
+}
+
+// For complete Mcast State Log, pass mDNStrue to mstatelog in LogMcastStateInfo()
+mDNSexport void LogMcastStateInfo(mDNS *const m, mDNSBool mflag, mDNSBool start, mDNSBool mstatelog)
+{
+    if (!mstatelog)
+    {
+        if (!all_requests)
+        {
+            LogMcastNoIdent("<None>");
+        }
+        else
+        {
+            request_state *req, *r;
+            for (req = all_requests; req; req=req->next)
+            {    
+                if (req->primary) // If this is a subbordinate operation, check that the parent is in the list
+                {    
+                    for (r = all_requests; r && r != req; r=r->next) 
+                        if (r == req->primary) 
+                            goto foundpar;
+                }    
+                // For non-subbordinate operations, and subbordinate operations that have lost their parent, write out their info
+                GetMcastClients(req);
+    foundpar:;
+            }
+            LogMcastNoIdent("--- MCAST RECORDS COUNT[%d] MCAST QUESTIONS COUNT[%d] ---", n_mrecords, n_mquests);
+            n_mrecords = n_mquests = 0; // Reset the values
+        }
+    }
+    else
+    {
+        static mDNSu32 i_mpktnum;
+        i_mcount = 0;
+        if (start)
+            mcount = 0;
+        // mcount is initialized to 0 when the PROF signal is sent since mcount could have
+        // wrong value if MulticastLogging is disabled and then re-enabled
+        LogMcastNoIdent("--- START MCAST STATE LOG ---");
+        if (!all_requests)
+        { 
+            mcount = 0;
+            LogMcastNoIdent("<None>");
+        } 
+        else 
+        {    
+            request_state *req, *r;
+            for (req = all_requests; req; req=req->next)
+            {    
+                if (req->primary) // If this is a subbordinate operation, check that the parent is in the list
+                {    
+                    for (r = all_requests; r && r != req; r=r->next) 
+                        if (r == req->primary) 
+                            goto foundparent;
+                    LogMcastNoIdent("%3d: Orphan operation; parent not found in request list", req->sd);
+                }    
+                // For non-subbordinate operations, and subbordinate operations that have lost their parent, write out their info
+                LogMcastClientInfo(req);
+    foundparent:;
+            }
+            if(!mcount) // To initially set mcount
+                mcount = i_mcount;    
+        }
+        if (mcount == 0)
+        {
+            i_mpktnum = m->MPktNum;
+            LogMcastNoIdent("--- MCOUNT[%d]: IMPKTNUM[%d] ---", mcount, i_mpktnum);
+        }
+        if (mflag)
+            LogMcastNoIdent("--- MCOUNT[%d]: CMPKTNUM[%d] - IMPKTNUM[%d] = [%d]PKTS ---", mcount, m->MPktNum, i_mpktnum, (m->MPktNum - i_mpktnum));    
+        LogMcastNoIdent("--- END MCAST STATE LOG ---");
+    }
+}
+
 mDNSlocal void abort_request(request_state *req)
 {
     if (req->terminate == (req_termination_fn) ~0)
 mDNSlocal void abort_request(request_state *req)
 {
     if (req->terminate == (req_termination_fn) ~0)
@@ -539,6 +683,9 @@ mDNSlocal AuthRecord *read_rr_from_ipc_msg(request_state *request, int GetTTL, i
         artype = AuthRecordLocalOnly;
     else if (InterfaceID == mDNSInterface_P2P)
         artype = AuthRecordP2P;
         artype = AuthRecordLocalOnly;
     else if (InterfaceID == mDNSInterface_P2P)
         artype = AuthRecordP2P;
+    else if ((InterfaceID == mDNSInterface_Any) && (flags & kDNSServiceFlagsIncludeP2P)
+            && (flags & kDNSServiceFlagsIncludeAWDL))
+        artype = AuthRecordAnyIncludeAWDLandP2P;
     else if ((InterfaceID == mDNSInterface_Any) && (flags & kDNSServiceFlagsIncludeP2P))
         artype = AuthRecordAnyIncludeP2P;
     else if ((InterfaceID == mDNSInterface_Any) && (flags & kDNSServiceFlagsIncludeAWDL))
     else if ((InterfaceID == mDNSInterface_Any) && (flags & kDNSServiceFlagsIncludeP2P))
         artype = AuthRecordAnyIncludeP2P;
     else if ((InterfaceID == mDNSInterface_Any) && (flags & kDNSServiceFlagsIncludeAWDL))
@@ -744,7 +891,16 @@ mDNSlocal void unlink_and_free_service_instance(service_instance *srv)
     if (srv->srs.RR_TXT.resrec.rdata != &srv->srs.RR_TXT.rdatastorage)
         freeL("TXT RData", srv->srs.RR_TXT.resrec.rdata);
 
     if (srv->srs.RR_TXT.resrec.rdata != &srv->srs.RR_TXT.rdatastorage)
         freeL("TXT RData", srv->srs.RR_TXT.resrec.rdata);
 
-    if (srv->subtypes) { freeL("ServiceSubTypes", srv->subtypes); srv->subtypes = NULL; }
+    if (srv->subtypes)
+    {
+        freeL("ServiceSubTypes", srv->subtypes);
+        srv->subtypes = NULL;
+    }
+    if (srv->srs.AnonData)
+    {
+        freeL("Anonymous", (void *)srv->srs.AnonData);
+        srv->srs.AnonData = NULL;
+    }
     freeL("service_instance", srv);
 }
 
     freeL("service_instance", srv);
 }
 
@@ -944,13 +1100,21 @@ mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord *rr, mStatus result)
 
         if (result)
         {
 
         if (result)
         {
-            // unlink from list, free memory
-            registered_record_entry **ptr = &request->u.reg_recs;
-            while (*ptr && (*ptr) != re) ptr = &(*ptr)->next;
-            if (!*ptr) { LogMsg("regrecord_callback - record not in list!"); return; }
-            *ptr = (*ptr)->next;
-            freeL("registered_record_entry AuthRecord regrecord_callback", re->rr);
-            freeL("registered_record_entry regrecord_callback", re);
+            // If this is a callback to a keepalive record, do not free it.
+            if (result == mStatus_BadStateErr)
+            {
+                LogInfo("regrecord_callback: Callback with error code mStatus_BadStateErr - not freeing the record.");
+            }
+            else
+            {
+                // unlink from list, free memory
+                registered_record_entry **ptr = &request->u.reg_recs;
+                while (*ptr && (*ptr) != re) ptr = &(*ptr)->next;
+                if (!*ptr) { LogMsg("regrecord_callback - record not in list!"); return; }
+                *ptr = (*ptr)->next;
+                freeL("registered_record_entry AuthRecord regrecord_callback", re->rr);
+                freeL("registered_record_entry regrecord_callback", re);
+             }
         }
         else
         {
         }
         else
         {
@@ -966,28 +1130,30 @@ mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord *rr, mStatus result)
     }
 }
 
     }
 }
 
-mDNSlocal pid_t get_peer_pid(int sock, char *pid_name_local) 
+// set_peer_pid() is called after mem is allocated for each new request in NewRequest()
+// This accounts for 2 places (connect_callback, request_callback)
+mDNSlocal void set_peer_pid(request_state *request)
 {
 {
-    pid_t           p = (pid_t) -1;
-    socklen_t       len = sizeof(p);
-    pid_name_local[0] = '\0';
-#ifdef LOCAL_PEERPID    
-    if (sock < 0) 
-        return -1;
+    pid_t           p    = (pid_t) -1;
+    socklen_t       len  = sizeof(p);
+    request->pid_name[0] = '\0';
+    request->process_id  = -1;
+#ifdef LOCAL_PEERPID  
+    if (request->sd < 0)  
+        return;
     // to extract the pid value
     // to extract the pid value
-    if (getsockopt(sock, SOL_LOCAL, LOCAL_PEERPID, &p, &len) != 0)
-        return -1;
+    if (getsockopt(request->sd, SOL_LOCAL, LOCAL_PEERPID, &p, &len) != 0)
+        return;
     // to extract the process name from the pid value
     if (proc_pidinfo(p, PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0)
     // to extract the process name from the pid value
     if (proc_pidinfo(p, PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0)
-        return -1;
-    mDNSPlatformStrCopy(pid_name_local, proc.pbsi_comm);
-    return p;  
+        return;
+    mDNSPlatformStrCopy(request->pid_name, proc.pbsi_comm);
+    request->process_id = p;
 #else   // !LOCAL_PEERPID
     len = 0;
 #else   // !LOCAL_PEERPID
     len = 0;
-    if (sock < 0) 
-        return -1;
-    LogInfo("get_peer_pid: Not Supported on this version of OS");
-    return -1;
+    if (request->sd < 0)
+        return;
+    LogInfo("set_peer_pid: Not Supported on this version of OS");
 #endif  // LOCAL_PEERPID
 }
 
 #endif  // LOCAL_PEERPID
 }
 
@@ -997,7 +1163,7 @@ mDNSlocal void connection_termination(request_state *request)
     // and terminate any subbordinate operations sharing this file descriptor
     request_state **req = &all_requests;
 
     // and terminate any subbordinate operations sharing this file descriptor
     request_state **req = &all_requests;
 
-    LogOperation("%3d: DNSServiceCreateConnection STOP PID[%d](%s)", request->sd, get_peer_pid(request->sd, pid_name), pid_name);
+    LogOperation("%3d: DNSServiceCreateConnection STOP PID[%d](%s)", request->sd, request->process_id, request->pid_name);
 
     while (*req)
     {
 
     while (*req)
     {
@@ -1018,7 +1184,7 @@ mDNSlocal void connection_termination(request_state *request)
     while (request->u.reg_recs)
     {
         registered_record_entry *ptr = request->u.reg_recs;
     while (request->u.reg_recs)
     {
         registered_record_entry *ptr = request->u.reg_recs;
-        LogOperation("%3d: DNSServiceRegisterRecord(%u %s) STOP PID[%d](%s)", request->sd, ptr->key, RRDisplayString(&mDNSStorage, &ptr->rr->resrec), get_peer_pid(request->sd, pid_name), pid_name);
+        LogOperation("%3d: DNSServiceRegisterRecord(%u %s) STOP PID[%d](%s)", request->sd, ptr->key, RRDisplayString(&mDNSStorage, &ptr->rr->resrec), request->process_id, request->pid_name);
                request->u.reg_recs = request->u.reg_recs->next;
         ptr->rr->RecordContext = NULL;
         if (ptr->external_advertise)
                request->u.reg_recs = request->u.reg_recs->next;
         ptr->rr->RecordContext = NULL;
         if (ptr->external_advertise)
@@ -1026,6 +1192,7 @@ mDNSlocal void connection_termination(request_state *request)
             ptr->external_advertise = mDNSfalse;
             external_stop_advertising_service(&ptr->rr->resrec, request->flags);
         }
             ptr->external_advertise = mDNSfalse;
             external_stop_advertising_service(&ptr->rr->resrec, request->flags);
         }
+        LogMcastS(&mDNSStorage, ptr->rr, request, reg_stop);
         mDNS_Deregister(&mDNSStorage, ptr->rr);     // Will free ptr->rr for us
         freeL("registered_record_entry/connection_termination", ptr);
     }
         mDNS_Deregister(&mDNSStorage, ptr->rr);     // Will free ptr->rr for us
         freeL("registered_record_entry/connection_termination", ptr);
     }
@@ -1070,7 +1237,8 @@ mDNSlocal mStatus handle_regrecord_request(request_state *request)
         }
         // allocate registration entry, link into list
         re = mallocL("registered_record_entry", sizeof(registered_record_entry));
         }
         // allocate registration entry, link into list
         re = mallocL("registered_record_entry", sizeof(registered_record_entry));
-        if (!re) FatalError("ERROR: malloc");
+        if (!re) 
+            FatalError("ERROR: malloc");
         re->key                   = request->hdr.reg_index;
         re->rr                    = rr;
         re->regrec_client_context = request->hdr.client_context;
         re->key                   = request->hdr.reg_index;
         re->rr                    = rr;
         re->regrec_client_context = request->hdr.client_context;
@@ -1080,16 +1248,18 @@ mDNSlocal mStatus handle_regrecord_request(request_state *request)
         rr->RecordCallback        = regrecord_callback;
 
         re->origInterfaceID = rr->resrec.InterfaceID;
         rr->RecordCallback        = regrecord_callback;
 
         re->origInterfaceID = rr->resrec.InterfaceID;
-        if (rr->resrec.InterfaceID == mDNSInterface_P2P) rr->resrec.InterfaceID = mDNSInterface_Any;
+        if (rr->resrec.InterfaceID == mDNSInterface_P2P) 
+            rr->resrec.InterfaceID = mDNSInterface_Any;
 #if 0
         if (!AuthorizedDomain(request, rr->resrec.name, AutoRegistrationDomains)) return (mStatus_NoError);
 #endif
         if (rr->resrec.rroriginalttl == 0)
             rr->resrec.rroriginalttl = DefaultTTLforRRType(rr->resrec.rrtype);
 
 #if 0
         if (!AuthorizedDomain(request, rr->resrec.name, AutoRegistrationDomains)) return (mStatus_NoError);
 #endif
         if (rr->resrec.rroriginalttl == 0)
             rr->resrec.rroriginalttl = DefaultTTLforRRType(rr->resrec.rrtype);
 
-        LogOperation("%3d: DNSServiceRegisterRecord(%u %s) START PID[%d](%s)", 
-               request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec), get_peer_pid(request->sd, pid_name), pid_name);
-       err = mDNS_Register(&mDNSStorage, rr);
+        LogOperation("%3d: DNSServiceRegisterRecord(%u %s) START PID[%d](%s)", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec), 
+                     request->process_id, request->pid_name);
+
+        err = mDNS_Register(&mDNSStorage, rr);
         if (err)
         {
             LogOperation("%3d: DNSServiceRegisterRecord(%u %s) ERROR (%d)", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec), err);
         if (err)
         {
             LogOperation("%3d: DNSServiceRegisterRecord(%u %s) ERROR (%d)", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec), err);
@@ -1098,6 +1268,7 @@ mDNSlocal mStatus handle_regrecord_request(request_state *request)
         }
         else
         {
         }
         else
         {
+            LogMcastS(&mDNSStorage, rr, request, reg_start); 
             re->next = request->u.reg_recs;
             request->u.reg_recs = re;
         }
             re->next = request->u.reg_recs;
             request->u.reg_recs = re;
         }
@@ -1109,14 +1280,18 @@ mDNSlocal void UpdateDeviceInfoRecord(mDNS *const m);
 
 mDNSlocal void regservice_termination_callback(request_state *request)
 {
 
 mDNSlocal void regservice_termination_callback(request_state *request)
 {
-    if (!request) { LogMsg("regservice_termination_callback context is NULL"); return; }
+    if (!request) 
+    { 
+        LogMsg("regservice_termination_callback context is NULL"); 
+        return; 
+    }
     while (request->u.servicereg.instances)
     {
         service_instance *p = request->u.servicereg.instances;
         request->u.servicereg.instances = request->u.servicereg.instances->next;
         // only safe to free memory if registration is not valid, i.e. deregister fails (which invalidates p)
     while (request->u.servicereg.instances)
     {
         service_instance *p = request->u.servicereg.instances;
         request->u.servicereg.instances = request->u.servicereg.instances->next;
         // only safe to free memory if registration is not valid, i.e. deregister fails (which invalidates p)
-        LogOperation("%3d: DNSServiceRegister(%##s, %u) STOP PID[%d](%s)",
-               request->sd, p->srs.RR_SRV.resrec.name->c, mDNSVal16(p->srs.RR_SRV.resrec.rdata->u.srv.port), get_peer_pid(request->sd, pid_name), pid_name);
+        LogOperation("%3d: DNSServiceRegister(%##s, %u) STOP PID[%d](%s)", request->sd, p->srs.RR_SRV.resrec.name->c, 
+                     mDNSVal16(p->srs.RR_SRV.resrec.rdata->u.srv.port), request->process_id, request->pid_name);
 
         external_stop_advertising_helper(p);
 
 
         external_stop_advertising_helper(p);
 
@@ -1126,11 +1301,18 @@ mDNSlocal void regservice_termination_callback(request_state *request)
         // We can't clear p->request *after* the calling mDNS_DeregisterService/unlink_and_free_service_instance
         // because by then we might have already freed p
         p->request = NULL;
         // We can't clear p->request *after* the calling mDNS_DeregisterService/unlink_and_free_service_instance
         // because by then we might have already freed p
         p->request = NULL;
-        if (mDNS_DeregisterService(&mDNSStorage, &p->srs)) unlink_and_free_service_instance(p);
-        // Don't touch service_instance *p after this -- it's likely to have been freed already
+        LogMcastS(&mDNSStorage, &p->srs.RR_SRV, request, reg_stop);
+        if (mDNS_DeregisterService(&mDNSStorage, &p->srs))
+        {
+            unlink_and_free_service_instance(p);
+            // Don't touch service_instance *p after this -- it's likely to have been freed already
+        }
     }
     if (request->u.servicereg.txtdata)
     }
     if (request->u.servicereg.txtdata)
-    { freeL("service_info txtdata", request->u.servicereg.txtdata); request->u.servicereg.txtdata = NULL; }
+    { 
+        freeL("service_info txtdata", request->u.servicereg.txtdata); 
+        request->u.servicereg.txtdata = NULL; 
+    }
     if (request->u.servicereg.autoname)
     {
         // Clear autoname before calling UpdateDeviceInfoRecord() so it doesn't mistakenly include this in its count of active autoname registrations
     if (request->u.servicereg.autoname)
     {
         // Clear autoname before calling UpdateDeviceInfoRecord() so it doesn't mistakenly include this in its count of active autoname registrations
@@ -1172,8 +1354,13 @@ mDNSlocal mStatus add_record_to_service(request_state *request, service_instance
         coreFlags |= coreFlagIncludeAWDL;
     
     result = mDNS_AddRecordToService(&mDNSStorage, srs, extra, &extra->r.rdatastorage, ttl, coreFlags);
         coreFlags |= coreFlagIncludeAWDL;
     
     result = mDNS_AddRecordToService(&mDNSStorage, srs, extra, &extra->r.rdatastorage, ttl, coreFlags);
-    if (result) { freeL("ExtraResourceRecord/add_record_to_service", extra); return result; }
-
+    if (result) 
+    { 
+        freeL("ExtraResourceRecord/add_record_to_service", extra); 
+        return result; 
+    }
+    LogMcastS(&mDNSStorage, &srs->RR_PTR, request, reg_start);
+     
     extra->ClientID = request->hdr.reg_index;
     if (   instance->external_advertise
            && callExternalHelpers(request->u.servicereg.InterfaceID, &instance->domain, request->flags))
     extra->ClientID = request->hdr.reg_index;
     if (   instance->external_advertise
            && callExternalHelpers(request->u.servicereg.InterfaceID, &instance->domain, request->flags))
@@ -1381,13 +1568,13 @@ mDNSlocal mStatus remove_record(request_state *request)
         external_stop_advertising_service(&e->rr->resrec, request->flags);
         e->external_advertise = mDNSfalse;
     }
         external_stop_advertising_service(&e->rr->resrec, request->flags);
         e->external_advertise = mDNSfalse;
     }
+    LogMcastS(&mDNSStorage, e->rr, request, reg_stop);
     err = mDNS_Deregister(&mDNSStorage, e->rr);     // Will free e->rr for us; we're responsible for freeing e
     if (err)
     {
         LogMsg("ERROR: remove_record, mDNS_Deregister: %d", err);
         freeL("registered_record_entry AuthRecord remove_record", e->rr);
     }
     err = mDNS_Deregister(&mDNSStorage, e->rr);     // Will free e->rr for us; we're responsible for freeing e
     if (err)
     {
         LogMsg("ERROR: remove_record, mDNS_Deregister: %d", err);
         freeL("registered_record_entry AuthRecord remove_record", e->rr);
     }
-
     freeL("registered_record_entry remove_record", e);
     return err;
 }
     freeL("registered_record_entry remove_record", e);
     return err;
 }
@@ -1445,13 +1632,28 @@ mDNSlocal mStatus handle_removerecord_request(request_state *request)
 // If there's a comma followed by another character,
 // FindFirstSubType overwrites the comma with a nul and returns the pointer to the next character.
 // Otherwise, it returns a pointer to the final nul at the end of the string
 // If there's a comma followed by another character,
 // FindFirstSubType overwrites the comma with a nul and returns the pointer to the next character.
 // Otherwise, it returns a pointer to the final nul at the end of the string
-mDNSlocal char *FindFirstSubType(char *p)
+mDNSlocal char *FindFirstSubType(char *p, char **AnonData)
 {
     while (*p)
     {
 {
     while (*p)
     {
-        if (p[0] == '\\' && p[1]) p += 2;
-        else if (p[0] == ',' && p[1]) { *p++ = 0; return(p); }
-        else p++;
+        if (p[0] == '\\' && p[1])
+        {
+             p += 2;
+        }
+        else if (p[0] == ',' && p[1])
+        {
+            *p++ = 0;
+            return(p);
+        }
+        else if (p[0] == ':' && p[1])
+        {
+            *p++ = 0;
+            *AnonData = p;
+        }
+        else
+        {
+            p++;
+        }
     }
     return(p);
 }
     }
     return(p);
 }
@@ -1479,10 +1681,10 @@ mDNSlocal char *FindNextSubType(char *p)
 }
 
 // Returns -1 if illegal subtype found
 }
 
 // Returns -1 if illegal subtype found
-mDNSexport mDNSs32 ChopSubTypes(char *regtype)
+mDNSexport mDNSs32 ChopSubTypes(char *regtype, char **AnonData)
 {
     mDNSs32 NumSubTypes = 0;
 {
     mDNSs32 NumSubTypes = 0;
-    char *stp = FindFirstSubType(regtype);
+    char *stp = FindFirstSubType(regtype, AnonData);
     while (stp && *stp)                 // If we found a comma...
     {
         if (*stp == ',') return(-1);
     while (stp && *stp)                 // If we found a comma...
     {
         if (*stp == ',') return(-1);
@@ -1493,9 +1695,42 @@ mDNSexport mDNSs32 ChopSubTypes(char *regtype)
     return(NumSubTypes);
 }
 
     return(NumSubTypes);
 }
 
-mDNSexport AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p)
+mDNSexport AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p, char **AnonData)
 {
     AuthRecord *st = mDNSNULL;
 {
     AuthRecord *st = mDNSNULL;
+    // 
+    // "p" is pointing at the regtype e.g., _http._tcp followed by ":<AnonData>" indicated
+    // by AnonData being non-NULL which is in turn follwed by ",<SubTypes>" indicated by
+    // NumSubTypes being non-zero. We need to skip the initial regtype to get to the actual
+    // data that we want. When we come here, ChopSubTypes has null terminated like this e.g.,
+    //
+    // _http._tcp<NULL><AnonData><NULL><SubType1><NULL><SubType2><NULL> etc.
+    //
+    // 1. If we have Anonymous data and subtypes, skip the regtype (e.g., "_http._tcp")
+    //    to get the AnonData and then skip the AnonData to get to the SubType.
+    //
+    // 2. If we have only SubTypes, skip the regtype to get to the SubType data.
+    //
+    // 3. If we have only AnonData, skip the regtype to get to the AnonData.
+    //
+    // 4. If we don't have AnonData or NumStypes, it is a noop.
+    //
+    if (AnonData)
+    {
+        int len;
+
+        // Skip the regtype
+        while (*p) p++;
+        p++;
+
+        len = strlen(p) + 1;
+        *AnonData = mallocL("Anonymous", len);
+        if (!(*AnonData))
+        {
+           return (mDNSNULL);
+        }
+        mDNSPlatformMemCopy(*AnonData, p, len);
+    }
     if (NumSubTypes)
     {
         mDNSs32 i;
     if (NumSubTypes)
     {
         mDNSs32 i;
@@ -1504,12 +1739,21 @@ mDNSexport AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p)
         for (i = 0; i < NumSubTypes; i++)
         {
             mDNS_SetupResourceRecord(&st[i], mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, AuthRecordAny, mDNSNULL, mDNSNULL);
         for (i = 0; i < NumSubTypes; i++)
         {
             mDNS_SetupResourceRecord(&st[i], mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, AuthRecordAny, mDNSNULL, mDNSNULL);
+            // First time through we skip the regtype or AnonData. Subsequently, the
+            // previous subtype.
             while (*p) p++;
             p++;
             if (!MakeDomainNameFromDNSNameString(&st[i].namestorage, p))
             while (*p) p++;
             p++;
             if (!MakeDomainNameFromDNSNameString(&st[i].namestorage, p))
-            { freeL("ServiceSubTypes", st); return(mDNSNULL); }
+            {
+                freeL("ServiceSubTypes", st);
+                if (*AnonData)
+                    freeL("AnonymousData", *AnonData);
+                return(mDNSNULL);
+            }
         }
     }
         }
     }
+    // If NumSubTypes is zero and AnonData is non-NULL, we still return NULL but AnonData has been
+    // initialized. The caller knows how to handle this.
     return(st);
 }
 
     return(st);
 }
 
@@ -1527,11 +1771,14 @@ mDNSlocal mStatus register_service_instance(request_state *request, const domain
     if (request->flags & kDNSServiceFlagsIncludeAWDL)
         coreFlags |= coreFlagIncludeAWDL;
 
     if (request->flags & kDNSServiceFlagsIncludeAWDL)
         coreFlags |= coreFlagIncludeAWDL;
 
-    // client guarantees that record names are unique
-    // we reuse this deprecated flag for his fucntion
-    if (request->flags & kDNSServiceFlagsForce)
+    // Client guarantees that record names are unique, so we can skip sending out initial
+    // probe messages.  Standard name conflict resolution is still done if a conflict is discovered.
+    if (request->flags & kDNSServiceFlagsKnownUnique)
         coreFlags |= coreFlagKnownUnique;
 
         coreFlags |= coreFlagKnownUnique;
 
+    if (request->flags & kDNSServiceFlagsWakeOnlyService)
+        coreFlags |= coreFlagWakeOnly;
+
     // 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
     // registrations scoped to a specific interface, so for the automatic domains we add we must *not* specify an interface.
     // 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
     // registrations scoped to a specific interface, so for the automatic domains we add we must *not* specify an interface.
@@ -1549,32 +1796,36 @@ mDNSlocal mStatus register_service_instance(request_state *request, const domain
         }
     }
 
         }
     }
 
-    if (mDNSStorage.KnownBugs & mDNS_KnownBug_LimitedIPv6)
-    {
-        // Special-case hack: On Mac OS X 10.6.x and earlier we don't advertise SMB service in AutoTunnel domains,
-        // because AutoTunnel services have to support IPv6, and in Mac OS X 10.6.x the SMB server does not.
-        // <rdar://problem/5482322> BTMM: Don't advertise SMB with BTMM because it doesn't support IPv6
-        if (SameDomainName(&request->u.servicereg.type, (const domainname *) "\x4" "_smb" "\x4" "_tcp"))
-        {
-            DomainAuthInfo *AuthInfo = GetAuthInfoForName(&mDNSStorage, domain);
-            if (AuthInfo && AuthInfo->AutoTunnel) return(kDNSServiceErr_Unsupported);
-        }
-    }
-
     instance = mallocL("service_instance", sizeof(*instance) + extra_size);
     if (!instance) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; }
 
     instance->next                          = mDNSNULL;
     instance->request                       = request;
     instance = mallocL("service_instance", sizeof(*instance) + extra_size);
     if (!instance) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; }
 
     instance->next                          = mDNSNULL;
     instance->request                       = request;
-    instance->subtypes                      = AllocateSubTypes(request->u.servicereg.num_subtypes, request->u.servicereg.type_as_string);
     instance->renameonmemfree               = 0;
     instance->clientnotified                = mDNSfalse;
     instance->default_local                 = (request->u.servicereg.default_domain && DomainIsLocal);
     instance->external_advertise            = mDNSfalse;
     AssignDomainName(&instance->domain, domain);
 
     instance->renameonmemfree               = 0;
     instance->clientnotified                = mDNSfalse;
     instance->default_local                 = (request->u.servicereg.default_domain && DomainIsLocal);
     instance->external_advertise            = mDNSfalse;
     AssignDomainName(&instance->domain, domain);
 
+    instance->srs.AnonData = mDNSNULL;
+    if (!request->u.servicereg.AnonData)
+    {
+        instance->subtypes = AllocateSubTypes(request->u.servicereg.num_subtypes, request->u.servicereg.type_as_string, mDNSNULL);
+    }
+    else
+    {
+        char *AnonData = mDNSNULL;
+        instance->subtypes = AllocateSubTypes(request->u.servicereg.num_subtypes, request->u.servicereg.type_as_string, &AnonData);
+        if (AnonData)
+            instance->srs.AnonData = (const mDNSu8 *)AnonData;    
+    }
+
     if (request->u.servicereg.num_subtypes && !instance->subtypes)
     if (request->u.servicereg.num_subtypes && !instance->subtypes)
-    { unlink_and_free_service_instance(instance); instance = NULL; FatalError("ERROR: malloc"); }
+    { 
+        unlink_and_free_service_instance(instance); 
+        instance = NULL; 
+        FatalError("ERROR: malloc"); 
+    }
 
     result = mDNS_RegisterService(&mDNSStorage, &instance->srs,
                                   &request->u.servicereg.name, &request->u.servicereg.type, domain,
 
     result = mDNS_RegisterService(&mDNSStorage, &instance->srs,
                                   &request->u.servicereg.name, &request->u.servicereg.type, domain,
@@ -1587,8 +1838,9 @@ mDNSlocal mStatus register_service_instance(request_state *request, const domain
     if (!result)
     {
         *ptr = instance;        // Append this to the end of our request->u.servicereg.instances list
     if (!result)
     {
         *ptr = instance;        // Append this to the end of our request->u.servicereg.instances list
-        LogOperation("%3d: DNSServiceRegister(%##s, %u) ADDED",
-                     instance->request->sd, instance->srs.RR_SRV.resrec.name->c, mDNSVal16(request->u.servicereg.port));
+        LogOperation("%3d: DNSServiceRegister(%##s, %u) ADDED", instance->request->sd, 
+                     instance->srs.RR_SRV.resrec.name->c, mDNSVal16(request->u.servicereg.port));
+        LogMcastS(&mDNSStorage, &instance->srs.RR_SRV, request, reg_start);
     }
     else
     {
     }
     else
     {
@@ -1663,6 +1915,55 @@ mDNSlocal void udsserver_default_reg_domain_changed(const DNameListElem *const d
     }
 }
 
     }
 }
 
+// Don't allow normal and anonymous registration to coexist.
+mDNSlocal mDNSBool CheckForMixedRegistrations(domainname *regtype, domainname *domain, mDNSBool AnonData)
+{
+    request_state *request;
+
+    // We only care about local domains where the anonymous extension is
+    // implemented.
+    if (!SameDomainName(domain, (const domainname *) "\x5" "local"))
+    {
+        return mDNStrue;
+    }
+
+    for (request = all_requests; request; request = request->next)
+    {
+        service_instance *ptr;
+
+        if (request->terminate != regservice_termination_callback) continue;
+        for (ptr = request->u.servicereg.instances; ptr ; ptr = ptr->next)
+        {
+            if (!SameDomainName(&ptr->domain, (const domainname *)"\x5" "local") ||
+                !SameDomainName(&request->u.servicereg.type, regtype))
+            {
+                continue;
+            }
+
+            // If we are about to register a anonymous registraion, we dont't want to
+            // allow the regular ones and vice versa.
+            if (AnonData)
+            {
+                if (!ptr->srs.AnonData)
+                {
+                    LogMsg("CheckForMixedRegistrations: Normal registration already exists for %##s", regtype->c);
+                    return mDNSfalse;
+                }
+            }
+            else
+            {
+                // Allow multiple regular registrations
+                if (ptr->srs.AnonData)
+                {
+                    LogMsg("CheckForMixedRegistrations: Anonymous registration already exists for %##s", regtype->c);
+                    return mDNSfalse;
+                }
+            }
+        }
+    }
+    return mDNStrue;
+}
+
 mDNSlocal mStatus handle_regservice_request(request_state *request)
 {
     char name[256]; // Lots of spare space for extra-long names that we'll auto-truncate down to 63 bytes
 mDNSlocal mStatus handle_regservice_request(request_state *request)
 {
     char name[256]; // Lots of spare space for extra-long names that we'll auto-truncate down to 63 bytes
@@ -1670,6 +1971,7 @@ mDNSlocal mStatus handle_regservice_request(request_state *request)
     char type_as_string[MAX_ESCAPED_DOMAIN_NAME];
     domainname d, srv;
     mStatus err;
     char type_as_string[MAX_ESCAPED_DOMAIN_NAME];
     domainname d, srv;
     mStatus err;
+    char *AnonData = mDNSNULL;
 
     DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
     mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
 
     DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
     mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
@@ -1719,9 +2021,26 @@ mDNSlocal mStatus handle_regservice_request(request_state *request)
     if (!request->msgptr) { LogMsg("%3d: DNSServiceRegister(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
 
     // Check for sub-types after the service type
     if (!request->msgptr) { LogMsg("%3d: DNSServiceRegister(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
 
     // Check for sub-types after the service type
-    request->u.servicereg.num_subtypes = ChopSubTypes(request->u.servicereg.type_as_string);    // Note: Modifies regtype string to remove trailing subtypes
+    request->u.servicereg.num_subtypes = ChopSubTypes(request->u.servicereg.type_as_string, &AnonData);    // Note: Modifies regtype string to remove trailing subtypes
     if (request->u.servicereg.num_subtypes < 0)
     if (request->u.servicereg.num_subtypes < 0)
-    { LogMsg("ERROR: handle_regservice_request - ChopSubTypes failed %s", request->u.servicereg.type_as_string); return(mStatus_BadParamErr); }
+    {
+        LogMsg("ERROR: handle_regservice_request - ChopSubTypes failed %s", request->u.servicereg.type_as_string);
+        return(mStatus_BadParamErr);
+    }
+    if (AnonData)
+    {
+        int AnonDataLen = strlen(AnonData);
+        if (AnonDataLen > MAX_ANONYMOUS_DATA)
+        {
+            LogMsg("ERROR: handle_regservice_request: AnonDataLen %d", AnonDataLen);
+            return(mStatus_BadParamErr);
+        }
+        request->u.servicereg.AnonData = mDNStrue;
+    }
+    else
+    {
+        request->u.servicereg.AnonData = mDNSfalse;
+    }
 
     // Don't try to construct "domainname t" until *after* ChopSubTypes has worked its magic
     if (!*request->u.servicereg.type_as_string || !MakeDomainNameFromDNSNameString(&request->u.servicereg.type, request->u.servicereg.type_as_string))
 
     // Don't try to construct "domainname t" until *after* ChopSubTypes has worked its magic
     if (!*request->u.servicereg.type_as_string || !MakeDomainNameFromDNSNameString(&request->u.servicereg.type, request->u.servicereg.type_as_string))
@@ -1757,6 +2076,12 @@ mDNSlocal mStatus handle_regservice_request(request_state *request)
         MakeDomainNameFromDNSNameString(&d, "local.");
     }
 
         MakeDomainNameFromDNSNameString(&d, "local.");
     }
 
+    // We don't allow the anonymous and the regular ones to coexist
+    if (!CheckForMixedRegistrations(&request->u.servicereg.type, &d, request->u.servicereg.AnonData))
+    {
+        return(mStatus_BadParamErr);
+    }
+
     if (!ConstructServiceName(&srv, &request->u.servicereg.name, &request->u.servicereg.type, &d))
     {
         LogMsg("ERROR: handle_regservice_request - Couldn't ConstructServiceName from, “%#s” “%##s” “%##s”",
     if (!ConstructServiceName(&srv, &request->u.servicereg.name, &request->u.servicereg.type, &d))
     {
         LogMsg("ERROR: handle_regservice_request - Couldn't ConstructServiceName from, “%#s” “%##s” “%##s”",
@@ -1775,13 +2100,13 @@ mDNSlocal mStatus handle_regservice_request(request_state *request)
     {
         int count = CountExistingRegistrations(&srv, request->u.servicereg.port);
         if (count)
     {
         int count = CountExistingRegistrations(&srv, request->u.servicereg.port);
         if (count)
-            LogMsg("Client application registered %d identical instances of service %##s port %u.",
-                   count+1, srv.c, mDNSVal16(request->u.servicereg.port));
+            LogMsg("Client application[%d](%s) registered %d identical instances of service %##s port %u.", request->process_id,
+                   request->pid_name, count+1, srv.c, mDNSVal16(request->u.servicereg.port));
     }
 
     LogOperation("%3d: DNSServiceRegister(%X, %d, \"%s\", \"%s\", \"%s\", \"%s\", %u) START PID[%d](%s)",
     }
 
     LogOperation("%3d: DNSServiceRegister(%X, %d, \"%s\", \"%s\", \"%s\", \"%s\", %u) START PID[%d](%s)",
-       request->sd, flags, interfaceIndex, name, request->u.servicereg.type_as_string, domain, host, 
-    mDNSVal16(request->u.servicereg.port), get_peer_pid(request->sd, pid_name), pid_name);
+                request->sd, flags, interfaceIndex, name, request->u.servicereg.type_as_string, domain, host, 
+                 mDNSVal16(request->u.servicereg.port), request->process_id, request->pid_name);
 
     // 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
 
     // 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
@@ -1821,7 +2146,7 @@ mDNSlocal mStatus handle_regservice_request(request_state *request)
 
 mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
 {
 
 mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
 {
-    const DNSServiceFlags flags = AddRecord ? kDNSServiceFlagsAdd : 0;
+    DNSServiceFlags flags = AddRecord ? kDNSServiceFlagsAdd : 0;
     request_state *req = question->QuestionContext;
     reply_state *rep;
     (void)m; // Unused
     request_state *req = question->QuestionContext;
     reply_state *rep;
     (void)m; // Unused
@@ -1829,6 +2154,11 @@ mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const Resourc
     if (answer->rrtype != kDNSType_PTR)
     { LogMsg("%3d: FoundInstance: Should not be called with rrtype %d (not a PTR record)", req->sd, answer->rrtype); return; }
 
     if (answer->rrtype != kDNSType_PTR)
     { LogMsg("%3d: FoundInstance: Should not be called with rrtype %d (not a PTR record)", req->sd, answer->rrtype); return; }
 
+    if (mDNSOpaque16IsZero(question->TargetQID) && (question->BrowseThreshold > 0) && (question->CurrentAnswers >= question->BrowseThreshold))
+    {
+        flags |= kDNSServiceFlagsThresholdReached;
+    }
+
     if (GenerateNTDResponse(&answer->rdata->u.name, answer->InterfaceID, req, &rep, browse_reply_op, flags, mStatus_NoError) != mStatus_NoError)
     {
         if (SameDomainName(&req->u.browser.regtype, (const domainname*)"\x09_services\x07_dns-sd\x04_udp"))
     if (GenerateNTDResponse(&answer->rdata->u.name, answer->InterfaceID, req, &rep, browse_reply_op, flags, mStatus_NoError) != mStatus_NoError)
     {
         if (SameDomainName(&req->u.browser.regtype, (const domainname*)"\x09_services\x07_dns-sd\x04_udp"))
@@ -1867,8 +2197,8 @@ mDNSlocal mStatus add_domain_to_browser(request_state *info, const domainname *d
     b = mallocL("browser_t", sizeof(*b));
     if (!b) return mStatus_NoMemoryErr;
     AssignDomainName(&b->domain, d);
     b = mallocL("browser_t", sizeof(*b));
     if (!b) return mStatus_NoMemoryErr;
     AssignDomainName(&b->domain, d);
-    err = mDNS_StartBrowse(&mDNSStorage, &b->q,
-                           &info->u.browser.regtype, d, info->u.browser.interface_id, info->flags, info->u.browser.ForceMCast, (info->flags & kDNSServiceFlagsBackgroundTrafficClass) != 0, FoundInstance, info);
+    err = mDNS_StartBrowse(&mDNSStorage, &b->q, &info->u.browser.regtype, d, info->u.browser.AnonData, info->u.browser.interface_id, info->flags, 
+                            info->u.browser.ForceMCast, (info->flags & kDNSServiceFlagsBackgroundTrafficClass) != 0, FoundInstance, info);
     if (err)
     {
         LogMsg("mDNS_StartBrowse returned %d for type %##s domain %##s", err, info->u.browser.regtype.c, d->c);
     if (err)
     {
         LogMsg("mDNS_StartBrowse returned %d for type %##s domain %##s", err, info->u.browser.regtype.c, d->c);
@@ -1878,8 +2208,9 @@ mDNSlocal mStatus add_domain_to_browser(request_state *info, const domainname *d
     {
         b->next = info->u.browser.browsers;
         info->u.browser.browsers = b;
     {
         b->next = info->u.browser.browsers;
         info->u.browser.browsers = b;
-        LogOperation("%3d: DNSServiceBrowse(%##s) START PID[%d](%s)", info->sd, b->q.qname.c, get_peer_pid(info->sd, pid_name), pid_name);
-
+        LogOperation("%3d: DNSServiceBrowse(%##s) START PID[%d](%s)", info->sd, b->q.qname.c, info->process_id, 
+                     info->pid_name);
+        LogMcastQ(&mDNSStorage, &b->q, info, q_start);
         if (callExternalHelpers(info->u.browser.interface_id, &b->domain, info->flags))
         {
             domainname tmp;
         if (callExternalHelpers(info->u.browser.interface_id, &b->domain, info->flags))
         {
             domainname tmp;
@@ -1893,6 +2224,14 @@ mDNSlocal mStatus add_domain_to_browser(request_state *info, const domainname *d
 
 mDNSlocal void browse_termination_callback(request_state *info)
 {
 
 mDNSlocal void browse_termination_callback(request_state *info)
 {
+    if (info->u.browser.default_domain)
+    {
+        // Stop the domain enumeration queries to discover the WAB legacy browse domains
+        LogInfo("%3d: DNSServiceBrowse Cancel WAB PID[%d](%s)", info->sd, info->process_id, info->pid_name);
+        uDNS_StopWABQueries(&mDNSStorage, UDNS_WAB_LBROWSE_QUERY);
+    }
+    if (info->u.browser.AnonData)
+        freeL("Anonymous", (void *)info->u.browser.AnonData);
     while (info->u.browser.browsers)
     {
         browser_t *ptr = info->u.browser.browsers;
     while (info->u.browser.browsers)
     {
         browser_t *ptr = info->u.browser.browsers;
@@ -1906,8 +2245,9 @@ mDNSlocal void browse_termination_callback(request_state *info)
         }
 
         info->u.browser.browsers = ptr->next;
         }
 
         info->u.browser.browsers = ptr->next;
-        LogOperation("%3d: DNSServiceBrowse(%##s) STOP PID[%d](%s)", info->sd, ptr->q.qname.c, get_peer_pid(info->sd, pid_name), pid_name);
+        LogOperation("%3d: DNSServiceBrowse(%##s) STOP PID[%d](%s)", info->sd, ptr->q.qname.c, info->process_id, info->pid_name);
         mDNS_StopBrowse(&mDNSStorage, &ptr->q);  // no need to error-check result
         mDNS_StopBrowse(&mDNSStorage, &ptr->q);  // no need to error-check result
+        LogMcastQ(&mDNSStorage, &ptr->q, info, q_stop);         
         freeL("browser_t/browse_termination_callback", ptr);
     }
 }
         freeL("browser_t/browse_termination_callback", ptr);
     }
 }
@@ -2077,6 +2417,8 @@ mDNSlocal void SetPrefsBrowseDomains(mDNS *m, DNameListElem *browseDomains, mDNS
     }
 }
 
     }
 }
 
+#if APPLE_OSX_mDNSResponder
+
 mDNSlocal void UpdateDeviceInfoRecord(mDNS *const m)
 {
     int num_autoname = 0;
 mDNSlocal void UpdateDeviceInfoRecord(mDNS *const m)
 {
     int num_autoname = 0;
@@ -2097,17 +2439,19 @@ mDNSlocal void UpdateDeviceInfoRecord(mDNS *const m)
     if (m->DeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered)
         if (num_autoname > 0)
         {
     if (m->DeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered)
         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, AuthRecordAny, mDNSNULL, mDNSNULL);
             ConstructServiceName(&m->DeviceInfo.namestorage, &m->nicelabel, &DeviceInfoName, &localdomain);
             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);
-            m->DeviceInfo.resrec.rdata->u.data[0] = 6 + len;    // "model=" plus the device string
-            m->DeviceInfo.resrec.rdlength         = 7 + len;    // One extra for the length byte at the start of the string
+            m->DeviceInfo.resrec.rdlength = initializeDeviceInfoTXT(m, m->DeviceInfo.resrec.rdata->u.data);
             LogOperation("UpdateDeviceInfoRecord   Register %##s", m->DeviceInfo.resrec.name);
             mDNS_Register(m, &m->DeviceInfo);
         }
 }
             LogOperation("UpdateDeviceInfoRecord   Register %##s", m->DeviceInfo.resrec.name);
             mDNS_Register(m, &m->DeviceInfo);
         }
 }
+#else   // APPLE_OSX_mDNSResponder
+mDNSlocal void UpdateDeviceInfoRecord(mDNS *const m)
+{
+    (void)m; // unused
+}
+#endif  // APPLE_OSX_mDNSResponder
 
 mDNSexport void udsserver_handle_configchange(mDNS *const m)
 {
 
 mDNSexport void udsserver_handle_configchange(mDNS *const m)
 {
@@ -2137,7 +2481,7 @@ mDNSexport void udsserver_handle_configchange(mDNS *const m)
 
     // Let the platform layer get the current DNS information
     mDNS_Lock(m);
 
     // Let the platform layer get the current DNS information
     mDNS_Lock(m);
-    mDNSPlatformSetDNSConfig(m, mDNSfalse, mDNSfalse, mDNSNULL, &RegDomains, &BrowseDomains);
+    mDNSPlatformSetDNSConfig(m, mDNSfalse, mDNSfalse, mDNSNULL, &RegDomains, &BrowseDomains, mDNSfalse);
     mDNS_Unlock(m);
 
     // Any automatic registration domains are also implicitly automatic browsing domains
     mDNS_Unlock(m);
 
     // Any automatic registration domains are also implicitly automatic browsing domains
@@ -2211,7 +2555,9 @@ mDNSlocal mStatus handle_browse_request(request_state *request)
     char regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME];
     domainname typedn, d, temp;
     mDNSs32 NumSubTypes;
     char regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME];
     domainname typedn, d, temp;
     mDNSs32 NumSubTypes;
+    char *AnonData = mDNSNULL;
     mStatus err = mStatus_NoError;
     mStatus err = mStatus_NoError;
+    int AnonDataLen;
 
     DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
     mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
 
     DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
     mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
@@ -2223,13 +2569,28 @@ mDNSlocal mStatus handle_browse_request(request_state *request)
 
     if (!request->msgptr) { LogMsg("%3d: DNSServiceBrowse(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
 
 
     if (!request->msgptr) { LogMsg("%3d: DNSServiceBrowse(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
 
-    if (domain[0] == '\0') uDNS_SetupSearchDomains(&mDNSStorage, UDNS_START_WAB_QUERY);
-
     request->flags = flags;
     typedn.c[0] = 0;
     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);
-    if (NumSubTypes == 1 && !AppendDNSNameString(&typedn, regtype + strlen(regtype) + 1)) return(mStatus_BadParamErr);
+    NumSubTypes = ChopSubTypes(regtype, &AnonData);    // Note: Modifies regtype string to remove trailing subtypes
+    if (NumSubTypes < 0 || NumSubTypes > 1)
+        return(mStatus_BadParamErr);
+    AnonDataLen = 0;
+    if (AnonData)
+    {
+        AnonDataLen = strlen(AnonData);
+        if (AnonDataLen > MAX_ANONYMOUS_DATA)
+        {
+            LogMsg("handle_browse_request: AnonDataLen %d", AnonDataLen);
+            return(mStatus_BadParamErr);
+        }
+        // Account for the null byte
+        AnonDataLen += 1;
+    }
+    if (NumSubTypes == 1)
+    {
+        if (!AppendDNSNameString(&typedn, regtype + strlen(regtype) + 1 + AnonDataLen))
+            return(mStatus_BadParamErr);
+    }
 
     if (!regtype[0] || !AppendDNSNameString(&typedn, regtype)) return(mStatus_BadParamErr);
 
 
     if (!regtype[0] || !AppendDNSNameString(&typedn, regtype)) return(mStatus_BadParamErr);
 
@@ -2245,8 +2606,24 @@ mDNSlocal mStatus handle_browse_request(request_state *request)
     request->u.browser.browsers = NULL;
 
     LogOperation("%3d: DNSServiceBrowse(%X, %d, \"%##s\", \"%s\") START PID[%d](%s)", 
     request->u.browser.browsers = NULL;
 
     LogOperation("%3d: DNSServiceBrowse(%X, %d, \"%##s\", \"%s\") START PID[%d](%s)", 
-    request->sd, request->flags, interfaceIndex, request->u.browser.regtype.c, domain, get_peer_pid(request->sd, pid_name), pid_name);
+    request->sd, request->flags, interfaceIndex, request->u.browser.regtype.c, domain, request->process_id, request->pid_name);
 
 
+    if (request->u.browser.default_domain)
+    {
+        // Start the domain enumeration queries to discover the WAB browse domains
+        LogInfo("%3d: DNSServiceBrowse Start WAB PID[%d](%s)", request->sd, request->process_id, request->pid_name);
+        uDNS_StartWABQueries(&mDNSStorage, UDNS_WAB_LBROWSE_QUERY);
+    }
+    request->u.browser.AnonData = mDNSNULL;
+    if (AnonData)
+    {
+        int len = strlen(AnonData) + 1;
+        request->u.browser.AnonData = mallocL("Anonymous", len);
+        if (!request->u.browser.AnonData)
+            return mStatus_NoMemoryErr;
+        else
+            mDNSPlatformMemCopy((void *)request->u.browser.AnonData, AnonData, len);
+    }
     // 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
     // browses to be added, and we'll need to cancel them before freeing this memory.
     // 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
     // browses to be added, and we'll need to cancel them before freeing this memory.
@@ -2338,10 +2715,12 @@ mDNSlocal void resolve_result_callback(mDNS *const m, DNSQuestion *question, con
 
 mDNSlocal void resolve_termination_callback(request_state *request)
 {
 
 mDNSlocal void resolve_termination_callback(request_state *request)
 {
-    LogOperation("%3d: DNSServiceResolve(%##s) STOP PID[%d](%s)", request->sd, request->u.resolve.qtxt.qname.c, get_peer_pid(request->sd, pid_name), pid_name);
+    LogOperation("%3d: DNSServiceResolve(%##s) STOP PID[%d](%s)", request->sd, request->u.resolve.qtxt.qname.c, request->process_id, request->pid_name);
     mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qtxt);
     mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv);
     mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qtxt);
     mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv);
-    if (request->u.resolve.external_advertise) external_stop_resolving_service(request->u.resolve.qsrv.InterfaceID, &request->u.resolve.qsrv.qname, request->flags);
+    LogMcastQ(&mDNSStorage, &request->u.resolve.qsrv, request, q_stop);
+    if (request->u.resolve.external_advertise) 
+        external_stop_resolving_service(request->u.resolve.qsrv.InterfaceID, &request->u.resolve.qsrv.qname, request->flags);
 }
 
 mDNSlocal mStatus handle_resolve_request(request_state *request)
 }
 
 mDNSlocal mStatus handle_resolve_request(request_state *request)
@@ -2399,10 +2778,13 @@ mDNSlocal mStatus handle_resolve_request(request_state *request)
     request->u.resolve.qsrv.RetryWithSearchDomains = mDNSfalse;
     request->u.resolve.qsrv.TimeoutQuestion  = 0;
     request->u.resolve.qsrv.WakeOnResolve    = (flags & kDNSServiceFlagsWakeOnResolve) != 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.UseBrackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0;
+    request->u.resolve.qsrv.UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0;
     request->u.resolve.qsrv.ValidationRequired = 0;
     request->u.resolve.qsrv.ValidatingResponse = 0;
     request->u.resolve.qsrv.ValidationRequired = 0;
     request->u.resolve.qsrv.ValidatingResponse = 0;
+    request->u.resolve.qsrv.ProxyQuestion    = 0;
     request->u.resolve.qsrv.qnameOrig        = mDNSNULL;
     request->u.resolve.qsrv.qnameOrig        = mDNSNULL;
+    request->u.resolve.qsrv.AnonInfo         = mDNSNULL;
+    request->u.resolve.qsrv.pid              = request->process_id;
     request->u.resolve.qsrv.QuestionCallback = resolve_result_callback;
     request->u.resolve.qsrv.QuestionContext  = request;
 
     request->u.resolve.qsrv.QuestionCallback = resolve_result_callback;
     request->u.resolve.qsrv.QuestionContext  = request;
 
@@ -2422,10 +2804,13 @@ mDNSlocal mStatus handle_resolve_request(request_state *request)
     request->u.resolve.qtxt.RetryWithSearchDomains = mDNSfalse;
     request->u.resolve.qtxt.TimeoutQuestion  = 0;
     request->u.resolve.qtxt.WakeOnResolve    = 0;
     request->u.resolve.qtxt.RetryWithSearchDomains = mDNSfalse;
     request->u.resolve.qtxt.TimeoutQuestion  = 0;
     request->u.resolve.qtxt.WakeOnResolve    = 0;
-    request->u.resolve.qtxt.UseBrackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0;
+    request->u.resolve.qtxt.UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0;
     request->u.resolve.qtxt.ValidationRequired = 0;
     request->u.resolve.qtxt.ValidatingResponse = 0;
     request->u.resolve.qtxt.ValidationRequired = 0;
     request->u.resolve.qtxt.ValidatingResponse = 0;
+    request->u.resolve.qtxt.ProxyQuestion    = 0;
     request->u.resolve.qtxt.qnameOrig        = mDNSNULL;
     request->u.resolve.qtxt.qnameOrig        = mDNSNULL;
+    request->u.resolve.qtxt.AnonInfo         = mDNSNULL;
+    request->u.resolve.qtxt.pid              = request->process_id;
     request->u.resolve.qtxt.QuestionCallback = resolve_result_callback;
     request->u.resolve.qtxt.QuestionContext  = request;
 
     request->u.resolve.qtxt.QuestionCallback = resolve_result_callback;
     request->u.resolve.qtxt.QuestionContext  = request;
 
@@ -2439,16 +2824,20 @@ mDNSlocal mStatus handle_resolve_request(request_state *request)
 
     // ask the questions
     LogOperation("%3d: DNSServiceResolve(%X %d %##s) START PID[%d](%s)", request->sd, flags, interfaceIndex, 
 
     // ask the questions
     LogOperation("%3d: DNSServiceResolve(%X %d %##s) START PID[%d](%s)", request->sd, flags, interfaceIndex, 
-       request->u.resolve.qsrv.qname.c, get_peer_pid(request->sd, pid_name), pid_name);
+                request->u.resolve.qsrv.qname.c, request->process_id, request->pid_name);
     err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qsrv);
     err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qsrv);
+    
     if (!err)
     {
         err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qtxt);
     if (!err)
     {
         err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qtxt);
-        if (err) mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv);
+        if (err)
+        { 
+            mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv);
+        }
         else
         {
             request->terminate = resolve_termination_callback;
         else
         {
             request->terminate = resolve_termination_callback;
-
+            LogMcastQ(&mDNSStorage, &request->u.resolve.qsrv, request, q_start);
             if (callExternalHelpers(InterfaceID, &fqdn, flags))
             {
                 request->u.resolve.external_advertise    = mDNStrue;
             if (callExternalHelpers(InterfaceID, &fqdn, flags))
             {
                 request->u.resolve.external_advertise    = mDNStrue;
@@ -2616,6 +3005,7 @@ mDNSlocal mDNSBool ShouldDeliverNegativeResponse(mDNS *const m, DNSQuestion *que
 // top-level domain
 mDNSlocal mStatus SendAdditionalQuery(DNSQuestion *q, request_state *request, mStatus err)
 {
 // top-level domain
 mDNSlocal mStatus SendAdditionalQuery(DNSQuestion *q, request_state *request, mStatus err)
 {
+#ifndef UNICAST_DISABLED
     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"))
     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"))
@@ -2704,17 +3094,26 @@ mDNSlocal mStatus SendAdditionalQuery(DNSQuestion *q, request_state *request, mS
                 q2->RetryWithSearchDomains = mDNSfalse;
                 q2->SearchListIndex = 0;
                 q2->TimeoutQuestion = 0;
                 q2->RetryWithSearchDomains = mDNSfalse;
                 q2->SearchListIndex = 0;
                 q2->TimeoutQuestion = 0;
+                q2->AnonInfo        = mDNSNULL;
+                q2->pid             = request->process_id;
             }
             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);
             }
             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);
+#else // !UNICAST_DISABLED
+    (void) q;
+    (void) request;
+    (void) err;
+    
+    return mStatus_NoError;
+#endif // !UNICAST_DISABLED
 }
 #endif // APPLE_OSX_mDNSResponder
 
 // This function tries to append a search domain if valid and possible. If so, returns true.
 }
 #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)
+mDNSlocal mDNSBool RetryQuestionWithSearchDomains(mDNS *const m, DNSQuestion *question, request_state *req, QC_result AddRecord)
 {
     int result;
     // RetryWithSearchDomains tells the core to call us back so that we can retry with search domains if there is no
 {
     int result;
     // RetryWithSearchDomains tells the core to call us back so that we can retry with search domains if there is no
@@ -2725,7 +3124,7 @@ mDNSlocal mDNSBool RetryQuestionWithSearchDomains(mDNS *const m, DNSQuestion *qu
     // 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 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)
+    if ((AddRecord != QC_suppressed) && question->SearchListIndex != -1 && question->AppendSearchDomains)
     {
         question->RetryWithSearchDomains = 0;
         result = AppendNewSearchDomain(m, question);
     {
         question->RetryWithSearchDomains = 0;
         result = AppendNewSearchDomain(m, question);
@@ -2757,67 +3156,237 @@ mDNSlocal mDNSBool RetryQuestionWithSearchDomains(mDNS *const m, DNSQuestion *qu
     }
     else
     {
     }
     else
     {
-        LogInfo("%3d: RetryQuestionWithSearchDomains: Not appending search domains - SuppressQuery %d, SearchListIndex %d, AppendSearchDomains %d", req->sd, question->SuppressQuery, question->SearchListIndex, question->AppendSearchDomains);
+        LogInfo("%3d: RetryQuestionWithSearchDomains: Not appending search domains - SuppressQuery %d, SearchListIndex %d, AppendSearchDomains %d", req->sd, AddRecord, question->SearchListIndex, question->AppendSearchDomains);
     }
     return mDNSfalse;
 }
 
     }
     return mDNSfalse;
 }
 
-mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+mDNSlocal void queryrecord_result_reply(mDNS *const m, request_state *req, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord,
+    DNSServiceErrorType error)
 {
     char name[MAX_ESCAPED_DOMAIN_NAME];
 {
     char name[MAX_ESCAPED_DOMAIN_NAME];
-    request_state *req = question->QuestionContext;
+    size_t len;
+    DNSServiceFlags flags = 0;
     reply_state *rep;
     char *data;
     reply_state *rep;
     char *data;
-    size_t len;
-    DNSServiceErrorType error = kDNSServiceErr_NoError;
-    DNSQuestion *q = mDNSNULL;
 
 
-#if APPLE_OSX_mDNSResponder
-    {
-        // 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;
+    ConvertDomainNameToCString(answer->name, name);
 
 
-        if (q && question->qtype != q->qtype && !SameDomainName(&question->qname, &q->qname))
-        {
-            mStatus err;
-            domainname *orig = question->qnameOrig;
+    LogOperation("%3d: %s(%##s, %s) %s %s", req->sd,
+                 req->hdr.op == query_request ? "DNSServiceQueryRecord" : "DNSServiceGetAddrInfo",
+                 question->qname.c, DNSTypeName(question->qtype), AddRecord ? "ADD" : "RMV", RRDisplayString(m, answer));
 
 
-            LogInfo("queryrecord_result_callback: Stopping q2 local %##s", question->qname.c);
-            mDNS_StopQuery(m, question);
-            question->QuestionContext = mDNSNULL;
+    len = sizeof(DNSServiceFlags);  // calculate reply data length
+    len += sizeof(mDNSu32);     // interface index
+    len += sizeof(DNSServiceErrorType);
+    len += strlen(name) + 1;
+    len += 3 * sizeof(mDNSu16); // type, class, rdlen
+    len += answer->rdlength;
+    len += sizeof(mDNSu32);     // TTL
 
 
-            // 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)
+    rep = create_reply(req->hdr.op == query_request ? query_reply_op : addrinfo_reply_op, len, req);
+
+    if (AddRecord)
+        flags |= kDNSServiceFlagsAdd;    
+    if (question->ValidationStatus != 0)
+    {
+        error =   kDNSServiceErr_NoError;
+        if (question->ValidationRequired && question->ValidationState == DNSSECValDone)
+        {
+            switch (question->ValidationStatus) //Set the dnssec flags to be passed on to the Apps here
             {
             {
-                LogInfo("queryrecord_result_callback: question %##s AppendSearchDomains zero", q->qname.c);
-                return;
+            case DNSSEC_Secure:
+                flags |= kDNSServiceFlagsSecure;
+                break;
+            case DNSSEC_Insecure:
+                flags |= kDNSServiceFlagsInsecure;
+                break;
+            case DNSSEC_Indeterminate:
+                flags |= kDNSServiceFlagsIndeterminate;
+                break;
+            case DNSSEC_Bogus:
+                flags |= kDNSServiceFlagsBogus;
+                break;
+            default:
+                LogMsg("queryrecord_result_reply unknown status %d for %##s", question->ValidationStatus, question->qname.c);
             }
             }
+        }
+    }
+    
+    rep->rhdr->flags = dnssd_htonl(flags);
+    // Call mDNSPlatformInterfaceIndexfromInterfaceID, but suppressNetworkChange (last argument). Otherwise, if the
+    // InterfaceID is not valid, then it simulates a "NetworkChanged" which in turn makes questions
+    // to be stopped and started including  *this* one. Normally the InterfaceID is valid. But when we
+    // are using the /etc/hosts entries to answer a question, the InterfaceID may not be known to the
+    // mDNS core . Eventually, we should remove the calls to "NetworkChanged" in
+    // mDNSPlatformInterfaceIndexfromInterfaceID when it can't find InterfaceID as ResourceRecords
+    // should not have existed to answer this question if the corresponding interface is not valid.
+    rep->rhdr->ifi   = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNStrue));
+    rep->rhdr->error = dnssd_htonl(error);
+
+    data = (char *)&rep->rhdr[1];
+
+    put_string(name,             &data);
+    put_uint16(answer->rrtype,   &data);
+    put_uint16(answer->rrclass,  &data);
+    put_uint16(answer->rdlength, &data);
+    // We need to use putRData here instead of the crude put_rdata function, because the crude put_rdata
+    // function just does a blind memory copy without regard to structures that may have holes in them.
+    if (answer->rdlength)
+        if (!putRData(mDNSNULL, (mDNSu8 *)data, (mDNSu8 *)rep->rhdr + len, answer))
+            LogMsg("queryrecord_result_reply putRData failed %d", (mDNSu8 *)rep->rhdr + len - (mDNSu8 *)data);
+    data += answer->rdlength;
+    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;
+    }
+    else if ((AddRecord == QC_add) && req->hdr.op == addrinfo_request)
+    {
+        // Note: We count all answers including LocalOnly e.g., /etc/hosts. If we
+        // exclude that, v4ans/v6ans will be zero and we would wrongly think that
+        // we did not answer questions and setup the status to deliver triggers.
+        if (question->qtype == kDNSType_A)
+            req->u.addrinfo.v4ans = 1;
+        if (question->qtype == kDNSType_AAAA)
+            req->u.addrinfo.v6ans = 1;
+    }
+    else if ((AddRecord == QC_add) && req->hdr.op == query_request)
+    {
+        if (question->qtype == kDNSType_A || question->qtype == kDNSType_AAAA)
+            req->u.queryrecord.ans = 1;
+    }
+
+#if APPLE_OSX_mDNSResponder
+#if !NO_WCF
+    CHECK_WCF_FUNCTION(WCFIsServerRunning)
+    {
+        struct xucred x;
+        socklen_t xucredlen = sizeof(x);
+
+        if (WCFIsServerRunning((WCFConnection *)m->WCF) && answer->rdlength != 0)
+        {
+            if (getsockopt(req->sd, 0, LOCAL_PEERCRED, &x, &xucredlen) >= 0 &&
+                (x.cr_version == XUCRED_VERSION))
+            {
+                struct sockaddr_storage addr;
+                const RDataBody2 *const rdb = (RDataBody2 *)answer->rdata->u.data;
+                addr.ss_len = 0;
+                if (answer->rrtype == kDNSType_A || answer->rrtype == kDNSType_AAAA)
+                {
+                    if (answer->rrtype == kDNSType_A)
+                    {
+                        struct sockaddr_in *sin = (struct sockaddr_in *)&addr;
+                        sin->sin_port = 0;
+                        if (!putRData(mDNSNULL, (mDNSu8 *)&sin->sin_addr, (mDNSu8 *)(&sin->sin_addr + sizeof(rdb->ipv4)), answer))
+                            LogMsg("queryrecord_result_reply: WCF AF_INET putRData failed");
+                        else
+                        {
+                            addr.ss_len = sizeof (struct sockaddr_in);
+                            addr.ss_family = AF_INET;
+                        }
+                    }
+                    else if (answer->rrtype == kDNSType_AAAA)
+                    {
+                        struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr;
+                        sin6->sin6_port = 0;
+                        if (!putRData(mDNSNULL, (mDNSu8 *)&sin6->sin6_addr, (mDNSu8 *)(&sin6->sin6_addr + sizeof(rdb->ipv6)), answer))
+                            LogMsg("queryrecord_result_reply: WCF AF_INET6 putRData failed");
+                        else
+                        {
+                            addr.ss_len = sizeof (struct sockaddr_in6);
+                            addr.ss_family = AF_INET6;
+                        }
+                    }
+                    if (addr.ss_len)
+                    {
+                        debugf("queryrecord_result_reply: Name %s, uid %u, addr length %d", name, x.cr_uid, addr.ss_len);
+                        CHECK_WCF_FUNCTION((WCFConnection *)WCFNameResolvesToAddr)
+                        {
+                            WCFNameResolvesToAddr(m->WCF, name, (struct sockaddr *)&addr, x.cr_uid);
+                        }
+                    }
+                }
+                else if (answer->rrtype == kDNSType_CNAME)
+                {
+                    domainname cname;
+                    char cname_cstr[MAX_ESCAPED_DOMAIN_NAME];
+                    if (!putRData(mDNSNULL, cname.c, (mDNSu8 *)(cname.c + MAX_DOMAIN_NAME), answer))
+                        LogMsg("queryrecord_result_reply: WCF CNAME putRData failed");
+                    else
+                    {
+                        ConvertDomainNameToCString(&cname, cname_cstr);
+                        CHECK_WCF_FUNCTION((WCFConnection *)WCFNameResolvesToAddr)
+                        {
+                            WCFNameResolvesToName(m->WCF, name, cname_cstr, x.cr_uid);
+                        }
+                    }
+                }
+            }
+            else my_perror("queryrecord_result_reply: ERROR: getsockopt LOCAL_PEERCRED");
+        }
+    }
+#endif
+#endif
+}
+
+mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+{
+    request_state *req = question->QuestionContext;
+    DNSServiceErrorType error = kDNSServiceErr_NoError;
+    DNSQuestion *q = mDNSNULL;
+
+#if APPLE_OSX_mDNSResponder
+    {
+        // 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))
+        {
+            mStatus err;
+            domainname *orig = question->qnameOrig;
+
+            LogInfo("queryrecord_result_callback: Stopping q2 local %##s", question->qname.c);
+            mDNS_StopQuery(m, question);
+            question->QuestionContext = mDNSNULL;
+
+            // We got a negative response for the SOA record indicating that .local does not exist.
+            // But we might have other search domains (that does not end in .local) that can be
+            // appended to this question. In that case, we want to retry the question. Otherwise,
+            // we don't want to try this question as unicast.
+            if (answer->RecordType == kDNSRecordTypePacketNegative && !q->AppendSearchDomains)
+            {
+                LogInfo("queryrecord_result_callback: question %##s AppendSearchDomains zero", q->qname.c);
+                return;
+            }
+
+            // If we got a non-negative answer for our "local SOA" test query, start an additional parallel unicast query
+            //
+            // Note: When we copy the original question, we copy everything including the AppendSearchDomains,
+            // RetryWithSearchDomains except for qnameOrig which can be non-NULL if the original question is
+            // e.g., somehost and then we appended e.g., ".local" and retried that question. See comment in
+            // SendAdditionalQuery as to how qnameOrig gets initialized.
+            *question              = *q;
+            question->InterfaceID  = mDNSInterface_Unicast;
+            question->ExpectUnique = mDNStrue;
+            question->qnameOrig    = orig;
 
 
-            // If we got a non-negative answer for our "local SOA" test query, start an additional parallel unicast query
-            //
-            // Note: When we copy the original question, we copy everything including the AppendSearchDomains,
-            // RetryWithSearchDomains except for qnameOrig which can be non-NULL if the original question is
-            // e.g., somehost and then we appended e.g., ".local" and retried that question. See comment in
-            // SendAdditionalQuery as to how qnameOrig gets initialized.
-            *question              = *q;
-            question->InterfaceID  = mDNSInterface_Unicast;
-            question->ExpectUnique = mDNStrue;
-            question->qnameOrig    = orig;
-
             LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast, context %p", req->sd, question->qname.c, DNSTypeName(question->qtype), question->QuestionContext);
 
             // If the original question timed out, its QuestionContext would already be set to NULL and that's what we copied above.
             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.
@@ -2839,14 +3408,14 @@ mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question,
             question->AppendLocalSearchDomains = 0;
         }
 
             question->AppendLocalSearchDomains = 0;
         }
 
-        if (q && AddRecord && (question->InterfaceID == mDNSInterface_Unicast) && !answer->rdlength)
+        if (q && AddRecord && AddRecord != QC_dnssec && (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 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))
+            if (RetryQuestionWithSearchDomains(m, question, req, AddRecord))
                 return;
             if (question->AppendSearchDomains && !question->AppendLocalSearchDomains && IsLocalDomain(&question->qname))
             {
                 return;
             if (question->AppendSearchDomains && !question->AppendLocalSearchDomains && IsLocalDomain(&question->qname))
             {
@@ -2863,6 +3432,18 @@ mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question,
     }
 #endif // APPLE_OSX_mDNSResponder
 
     }
 #endif // APPLE_OSX_mDNSResponder
 
+    // If a query is being suppressed for some reason, we don't have to do any other
+    // processing.
+    //
+    // Note: We don't check for "SuppressQuery" and instead use QC_suppressed because
+    // the "core" needs to temporarily turn off SuppressQuery to answer this query.
+    if (AddRecord == QC_suppressed)
+    {
+        LogInfo("queryrecord_result_callback: Suppressed question %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
+        queryrecord_result_reply(m, req, question, answer, AddRecord, kDNSServiceErr_NoSuchRecord);
+        return;
+    }
+
     if (answer->RecordType == kDNSRecordTypePacketNegative)
     {
         // If this question needs to be timed out and we have reached the stop time, mark
     if (answer->RecordType == kDNSRecordTypePacketNegative)
     {
         // If this question needs to be timed out and we have reached the stop time, mark
@@ -2893,26 +3474,30 @@ mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question,
                 // Sanity check: "q" will be set only if "question" is the .local unicast query.
                 if (!q)
                 {
                 // Sanity check: "q" will be set only if "question" is the .local unicast query.
                 if (!q)
                 {
-                    LogMsg("queryrecord_result_callback: ERROR!! answering multicast question with unicast cache record");
+                    LogMsg("queryrecord_result_callback: ERROR!! answering multicast question %s with unicast cache record",
+                        RRDisplayString(m, answer));
                     return;
                 }
                     return;
                 }
+#if APPLE_OSX_mDNSResponder
                 if (!ShouldDeliverNegativeResponse(m, question))
                 {
                     return;
                 }
                 if (!ShouldDeliverNegativeResponse(m, question))
                 {
                     return;
                 }
-                LogInfo("queryrecord_result_callback:Question %##s (%s) answering local with negative unicast response (found positive record)", question->qname.c, DNSTypeName(question->qtype));
+#endif  // APPLE_OSX_mDNSResponder
+                LogInfo("queryrecord_result_callback:Question %##s (%s) answering local with negative unicast response", question->qname.c,
+                    DNSTypeName(question->qtype));
             }
             error = kDNSServiceErr_NoSuchRecord;
         }
             }
             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 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 this response is forced e.g., dnssec validation result
     if (error != kDNSServiceErr_Timeout)
     {
     if (error != kDNSServiceErr_Timeout)
     {
-        if (!q && !answer->InterfaceID && !answer->rdlength && AddRecord)
+        if (!q && !answer->InterfaceID && !answer->rdlength && AddRecord && AddRecord != QC_dnssec)
         {
             // 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
         {
             // 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
@@ -2920,7 +3505,7 @@ mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question,
             // 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));
             // 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))
+            if (RetryQuestionWithSearchDomains(m, question, req, AddRecord))
             {
                 // Note: We need to call SendAdditionalQuery every time after appending a search domain as .local could
                 // be anywhere in the search domain list.
             {
                 // Note: We need to call SendAdditionalQuery every time after appending a search domain as .local could
                 // be anywhere in the search domain list.
@@ -2933,136 +3518,17 @@ mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question,
             }
         }
     }
             }
         }
     }
-
-    ConvertDomainNameToCString(answer->name, name);
-
-    LogOperation("%3d: %s(%##s, %s) %s %s", req->sd,
-                 req->hdr.op == query_request ? "DNSServiceQueryRecord" : "DNSServiceGetAddrInfo",
-                 question->qname.c, DNSTypeName(question->qtype), AddRecord ? "ADD" : "RMV", RRDisplayString(m, answer));
-
-    len = sizeof(DNSServiceFlags);  // calculate reply data length
-    len += sizeof(mDNSu32);     // interface index
-    len += sizeof(DNSServiceErrorType);
-    len += strlen(name) + 1;
-    len += 3 * sizeof(mDNSu16); // type, class, rdlen
-    len += answer->rdlength;
-    len += sizeof(mDNSu32);     // TTL
-
-    rep = create_reply(req->hdr.op == query_request ? query_reply_op : addrinfo_reply_op, len, req);
-
-    rep->rhdr->flags = dnssd_htonl(AddRecord ? kDNSServiceFlagsAdd : 0);
-    // Call mDNSPlatformInterfaceIndexfromInterfaceID, but suppressNetworkChange (last argument). Otherwise, if the
-    // InterfaceID is not valid, then it simulates a "NetworkChanged" which in turn makes questions
-    // to be stopped and started including  *this* one. Normally the InterfaceID is valid. But when we
-    // are using the /etc/hosts entries to answer a question, the InterfaceID may not be known to the
-    // mDNS core . Eventually, we should remove the calls to "NetworkChanged" in
-    // mDNSPlatformInterfaceIndexfromInterfaceID when it can't find InterfaceID as ResourceRecords
-    // should not have existed to answer this question if the corresponding interface is not valid.
-    rep->rhdr->ifi   = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNStrue));
-    rep->rhdr->error = dnssd_htonl(error);
-
-    data = (char *)&rep->rhdr[1];
-
-    put_string(name,             &data);
-    put_uint16(answer->rrtype,   &data);
-    put_uint16(answer->rrclass,  &data);
-    put_uint16(answer->rdlength, &data);
-    // We need to use putRData here instead of the crude put_rdata function, because the crude put_rdata
-    // function just does a blind memory copy without regard to structures that may have holes in them.
-    if (answer->rdlength)
-        if (!putRData(mDNSNULL, (mDNSu8 *)data, (mDNSu8 *)rep->rhdr + len, answer))
-            LogMsg("queryrecord_result_callback putRData failed %d", (mDNSu8 *)rep->rhdr + len - (mDNSu8 *)data);
-    data += answer->rdlength;
-    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)
-    {
-        struct xucred x;
-        socklen_t xucredlen = sizeof(x);
-
-        if (WCFIsServerRunning((WCFConnection *)m->WCF) && answer->rdlength != 0)
-        {
-            if (getsockopt(req->sd, 0, LOCAL_PEERCRED, &x, &xucredlen) >= 0 &&
-                (x.cr_version == XUCRED_VERSION))
-            {
-                struct sockaddr_storage addr;
-                const RDataBody2 *const rdb = (RDataBody2 *)answer->rdata->u.data;
-                addr.ss_len = 0;
-                if (answer->rrtype == kDNSType_A || answer->rrtype == kDNSType_AAAA)
-                {
-                    if (answer->rrtype == kDNSType_A)
-                    {
-                        struct sockaddr_in *sin = (struct sockaddr_in *)&addr;
-                        sin->sin_port = 0;
-                        if (!putRData(mDNSNULL, (mDNSu8 *)&sin->sin_addr, (mDNSu8 *)(&sin->sin_addr + sizeof(rdb->ipv4)), answer))
-                            LogMsg("queryrecord_result_callback: WCF AF_INET putRData failed");
-                        else
-                        {
-                            addr.ss_len = sizeof (struct sockaddr_in);
-                            addr.ss_family = AF_INET;
-                        }
-                    }
-                    else if (answer->rrtype == kDNSType_AAAA)
-                    {
-                        struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr;
-                        sin6->sin6_port = 0;
-                        if (!putRData(mDNSNULL, (mDNSu8 *)&sin6->sin6_addr, (mDNSu8 *)(&sin6->sin6_addr + sizeof(rdb->ipv6)), answer))
-                            LogMsg("queryrecord_result_callback: WCF AF_INET6 putRData failed");
-                        else
-                        {
-                            addr.ss_len = sizeof (struct sockaddr_in6);
-                            addr.ss_family = AF_INET6;
-                        }
-                    }
-                    if (addr.ss_len)
-                    {
-                        debugf("queryrecord_result_callback: Name %s, uid %u, addr length %d", name, x.cr_uid, addr.ss_len);
-                        CHECK_WCF_FUNCTION((WCFConnection *)WCFNameResolvesToAddr)
-                        {
-                            WCFNameResolvesToAddr(m->WCF, name, (struct sockaddr *)&addr, x.cr_uid);
-                        }
-                    }
-                }
-                else if (answer->rrtype == kDNSType_CNAME)
-                {
-                    domainname cname;
-                    char cname_cstr[MAX_ESCAPED_DOMAIN_NAME];
-                    if (!putRData(mDNSNULL, cname.c, (mDNSu8 *)(cname.c + MAX_DOMAIN_NAME), answer))
-                        LogMsg("queryrecord_result_callback: WCF CNAME putRData failed");
-                    else
-                    {
-                        ConvertDomainNameToCString(&cname, cname_cstr);
-                        CHECK_WCF_FUNCTION((WCFConnection *)WCFNameResolvesToAddr)
-                        {
-                            WCFNameResolvesToName(m->WCF, name, cname_cstr, x.cr_uid);
-                        }
-                    }
-                }
-            }
-            else my_perror("queryrecord_result_callback: ERROR: getsockopt LOCAL_PEERCRED");
-        }
-    }
-#endif
-#endif
+    queryrecord_result_reply(m, req, question, answer, AddRecord, error);
 }
 
 mDNSlocal void queryrecord_termination_callback(request_state *request)
 {
     LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) STOP PID[%d](%s)",
 }
 
 mDNSlocal void queryrecord_termination_callback(request_state *request)
 {
     LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) STOP PID[%d](%s)",
-               request->sd, request->u.queryrecord.q.qname.c, DNSTypeName(request->u.queryrecord.q.qtype), get_peer_pid(request->sd, pid_name), pid_name);
+               request->sd, request->u.queryrecord.q.qname.c, DNSTypeName(request->u.queryrecord.q.qtype), request->process_id, request->pid_name);
     if (request->u.queryrecord.q.QuestionContext)
     {
         mDNS_StopQuery(&mDNSStorage, &request->u.queryrecord.q);  // no need to error check
     if (request->u.queryrecord.q.QuestionContext)
     {
         mDNS_StopQuery(&mDNSStorage, &request->u.queryrecord.q);  // no need to error check
+        LogMcastQ(&mDNSStorage, &request->u.queryrecord.q, request, q_stop);
         request->u.queryrecord.q.QuestionContext = mDNSNULL;
     }
     else
         request->u.queryrecord.q.QuestionContext = mDNSNULL;
     }
     else
@@ -3088,6 +3554,7 @@ mDNSlocal void queryrecord_termination_callback(request_state *request)
         {
             LogInfo("queryrecord_termination_callback: Stopping q2 %##s", request->u.queryrecord.q2->qname.c);
             mDNS_StopQuery(&mDNSStorage, request->u.queryrecord.q2);
         {
             LogInfo("queryrecord_termination_callback: Stopping q2 %##s", request->u.queryrecord.q2->qname.c);
             mDNS_StopQuery(&mDNSStorage, request->u.queryrecord.q2);
+            LogMcastQ(&mDNSStorage, request->u.queryrecord.q2, request, q_stop);
         }
         else
         {
         }
         else
         {
@@ -3103,6 +3570,50 @@ mDNSlocal void queryrecord_termination_callback(request_state *request)
         freeL("queryrecord Q2", request->u.queryrecord.q2);
         request->u.queryrecord.q2 = mDNSNULL;
     }
         freeL("queryrecord Q2", request->u.queryrecord.q2);
         request->u.queryrecord.q2 = mDNSNULL;
     }
+#if APPLE_OSX_mDNSResponder
+    {
+        if (request->u.queryrecord.ans)
+        {
+            DNSQuestion *v4q, *v6q;
+            // If we are receiving poisitive answers, provide the hint to the
+            // upper layer.
+            v4q = v6q = mDNSNULL;
+            if (request->u.queryrecord.q.qtype == kDNSType_A)
+                v4q = &request->u.queryrecord.q;
+            else if (request->u.queryrecord.q.qtype == kDNSType_AAAA)
+                v6q = &request->u.queryrecord.q;
+            mDNSPlatformTriggerDNSRetry(&mDNSStorage, v4q, v6q);
+        }
+    }
+#endif // APPLE_OSX_mDNSResponder
+}
+
+mDNSlocal void SetQuestionPolicy(DNSQuestion *q, request_state *req)
+{
+    int i;
+
+    // The policy is either based on pid or UUID. Pass a zero pid
+    // to the "core" if the UUID is valid. If we always pass the pid,
+    // then the "core" needs to determine whether the uuid is valid 
+    // by examining all the 16 bytes at the time of the policy
+    // check and also when setting the delegate socket option. Also, it
+    // requires that we zero out the uuid wherever the question is
+    // initialized to make sure that it is not interpreted as valid.
+    // To prevent these intrusive changes, just pass a zero pid to indicate
+    // that pid is not valid when uuid is valid. In future if we need the
+    // pid in the question, we will reevaluate this strategy.
+    if (req->validUUID)
+    {
+        for (i = 0; i < UUID_SIZE; i++)
+        {
+            q->uuid[i] = req->uuid[i];
+        }
+        q->pid = 0;
+    }
+    else
+    {
+        q->pid = req->process_id;
+    }
 }
 
 mDNSlocal mStatus handle_queryrecord_request(request_state *request)
 }
 
 mDNSlocal mStatus handle_queryrecord_request(request_state *request)
@@ -3143,24 +3654,37 @@ mDNSlocal mStatus handle_queryrecord_request(request_state *request)
     q->SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable   ) != 0;
     q->TimeoutQuestion  = (flags & kDNSServiceFlagsTimeout            ) != 0;
     q->WakeOnResolve    = 0;
     q->SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable   ) != 0;
     q->TimeoutQuestion  = (flags & kDNSServiceFlagsTimeout            ) != 0;
     q->WakeOnResolve    = 0;
-    q->UseBrackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0;
-    q->ValidationRequired = 0;
+    q->UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0;
+    if ((flags & kDNSServiceFlagsValidate) != 0)
+        q->ValidationRequired = DNSSEC_VALIDATION_SECURE;
+    else if ((flags & kDNSServiceFlagsValidateOptional) != 0)
+        q->ValidationRequired = DNSSEC_VALIDATION_SECURE_OPTIONAL;
     q->ValidatingResponse = 0;
     q->ValidatingResponse = 0;
+    q->ProxyQuestion    = 0;
+    q->AnonInfo = mDNSNULL;
     q->QuestionCallback = queryrecord_result_callback;
     q->QuestionContext  = request;
     q->SearchListIndex  = 0;
 
     q->QuestionCallback = queryrecord_result_callback;
     q->QuestionContext  = request;
     q->SearchListIndex  = 0;
 
+    q->DNSSECAuthInfo = mDNSNULL;
+    q->DAIFreeCallback = mDNSNULL;
+
+    //Turn off dnssec validation for local domains and Question Types: RRSIG/ANY(ANY Type is not supported yet)
+    if ((IsLocalDomain(&q->qname)) || (q->qtype == kDNSServiceType_RRSIG) || (q->qtype == kDNSServiceType_ANY))
+        q->ValidationRequired = 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.
     //
     // 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.
+    // 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.
+    // For DNSSEC questions, append search domains only if kDNSServiceFlagsValidateOptional is set.
 
 
-    if ((rrtype == kDNSType_A || rrtype == kDNSType_AAAA) && name[strlen(name) - 1] != '.' &&
-        (AlwaysAppendSearchDomains || CountLabels(&q->qname) == 1))
+    if ((!(q->ValidationRequired == DNSSEC_VALIDATION_SECURE)) && (!(q->ValidationRequired == DNSSEC_VALIDATION_INSECURE)) 
+        && (rrtype == kDNSType_A || rrtype == kDNSType_AAAA) && name[strlen(name) - 1] != '.' &&
+        (AlwaysAppendSearchDomains || CountLabels(&q->qname) == 1)) 
     {
         q->AppendSearchDomains = 1;
         q->AppendLocalSearchDomains = 1;
     {
         q->AppendSearchDomains = 1;
         q->AppendLocalSearchDomains = 1;
@@ -3177,14 +3701,18 @@ mDNSlocal mStatus handle_queryrecord_request(request_state *request)
     // the cache
     q->RetryWithSearchDomains = ApplySearchDomainsFirst(q) ? 1 : 0;
     q->qnameOrig        = mDNSNULL;
     // the cache
     q->RetryWithSearchDomains = ApplySearchDomainsFirst(q) ? 1 : 0;
     q->qnameOrig        = mDNSNULL;
+    SetQuestionPolicy(q, request);
 
     LogOperation("%3d: DNSServiceQueryRecord(%X, %d, %##s, %s) START PID[%d](%s)", 
 
     LogOperation("%3d: DNSServiceQueryRecord(%X, %d, %##s, %s) START PID[%d](%s)", 
-               request->sd, flags, interfaceIndex, q->qname.c, DNSTypeName(q->qtype), get_peer_pid(request->sd, pid_name), pid_name);
+               request->sd, flags, interfaceIndex, q->qname.c, DNSTypeName(q->qtype), request->process_id, request->pid_name);
     err = mDNS_StartQuery(&mDNSStorage, q);
     err = mDNS_StartQuery(&mDNSStorage, q);
-    if (err) LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q->qname.c, DNSTypeName(q->qtype), (int)err);
+   
+    if (err) 
+        LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q->qname.c, DNSTypeName(q->qtype), (int)err);
     else
     {
         request->terminate = queryrecord_termination_callback;
     else
     {
         request->terminate = queryrecord_termination_callback;
+        LogMcastQ(&mDNSStorage, q, request, q_start);
         if (callExternalHelpers(q->InterfaceID, &q->qname, flags))
         {
             LogInfo("handle_queryrecord_request: calling external_start_browsing_for_service()");
         if (callExternalHelpers(q->InterfaceID, &q->qname, flags))
         {
             LogInfo("handle_queryrecord_request: calling external_start_browsing_for_service()");
@@ -3228,6 +3756,17 @@ mDNSlocal reply_state *format_enumeration_reply(request_state *request,
 
 mDNSlocal void enum_termination_callback(request_state *request)
 {
 
 mDNSlocal void enum_termination_callback(request_state *request)
 {
+    // Stop the domain enumeration queries to discover the WAB Browse/Registration domains
+    if (request->u.enumeration.flags & kDNSServiceFlagsRegistrationDomains)
+    {
+        LogInfo("%3d: DNSServiceEnumeration Cancel WAB Registration PID[%d](%s)", request->sd, request->process_id, request->pid_name);
+        uDNS_StopWABQueries(&mDNSStorage, UDNS_WAB_REG_QUERY);
+    }
+    else
+    {
+        LogInfo("%3d: DNSServiceEnumeration Cancel WAB Browse PID[%d](%s)", request->sd, request->process_id, request->pid_name);
+        uDNS_StopWABQueries(&mDNSStorage, UDNS_WAB_BROWSE_QUERY);
+    }
     mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_all);
     mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_default);
 }
     mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_all);
     mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_default);
 }
@@ -3283,13 +3822,8 @@ mDNSlocal mStatus handle_enum_request(request_state *request)
     if (!request->msgptr)
     { LogMsg("%3d: DNSServiceEnumerateDomains(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
 
     if (!request->msgptr)
     { LogMsg("%3d: DNSServiceEnumerateDomains(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
 
-    // allocate context structures
-    uDNS_SetupSearchDomains(&mDNSStorage, UDNS_START_WAB_QUERY);
-
-#if 0
-    // mark which kind of enumeration we're doing so we can (de)authorize certain domains
+    // mark which kind of enumeration we're doing so that we know what domain enumeration queries to stop
     request->u.enumeration.flags = reg;
     request->u.enumeration.flags = reg;
-#endif
 
     // enumeration requires multiple questions, so we must link all the context pointers so that
     // necessary context can be reached from the callbacks
 
     // enumeration requires multiple questions, so we must link all the context pointers so that
     // necessary context can be reached from the callbacks
@@ -3310,6 +3844,20 @@ mDNSlocal mStatus handle_enum_request(request_state *request)
         if (err) mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_all);
         else request->terminate = enum_termination_callback;
     }
         if (err) mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_all);
         else request->terminate = enum_termination_callback;
     }
+    if (!err)
+    {
+        // Start the domain enumeration queries to discover the WAB Browse/Registration domains
+        if (reg)
+        {
+            LogInfo("%3d: DNSServiceEnumerateDomains Start WAB Registration PID[%d](%s)", request->sd, request->process_id, request->pid_name);
+            uDNS_StartWABQueries(&mDNSStorage, UDNS_WAB_REG_QUERY);
+        }
+        else 
+        {
+            LogInfo("%3d: DNSServiceEnumerateDomains Start WAB Browse PID[%d](%s)", request->sd, request->process_id, request->pid_name);
+            uDNS_StartWABQueries(&mDNSStorage, UDNS_WAB_BROWSE_QUERY);
+        }
+    }
 
     return(err);
 }
 
     return(err);
 }
@@ -3370,7 +3918,7 @@ mDNSlocal mStatus handle_release_request(request_state *request)
     }
 
     LogOperation("%3d: PeerConnectionRelease(%X %##s) START PID[%d](%s)",
     }
 
     LogOperation("%3d: PeerConnectionRelease(%X %##s) START PID[%d](%s)",
-        request->sd, flags, instance.c, get_peer_pid(request->sd, pid_name), pid_name);
+                 request->sd, flags, instance.c, request->process_id, request->pid_name);
     
     external_connection_release(&instance);
     return(err);
     
     external_connection_release(&instance);
     return(err);
@@ -3413,17 +3961,121 @@ mDNSlocal void handle_getproperty_request(request_state *request)
     char prop[256];
     if (get_string(&request->msgptr, request->msgend, prop, sizeof(prop)) >= 0)
     {
     char prop[256];
     if (get_string(&request->msgptr, request->msgend, prop, sizeof(prop)) >= 0)
     {
-        LogOperation("%3d: DNSServiceGetProperty(%s)", request->sd, prop);
-        if (!strcmp(prop, kDNSServiceProperty_DaemonVersion))
+        LogOperation("%3d: DNSServiceGetProperty(%s)", request->sd, prop);
+        if (!strcmp(prop, kDNSServiceProperty_DaemonVersion))
+        {
+            DaemonVersionReply x = { 0, dnssd_htonl(4), dnssd_htonl(_DNS_SD_H) };
+            send_all(request->sd, (const char *)&x, sizeof(x));
+            return;
+        }
+    }
+
+    // If we didn't recogize the requested property name, return BadParamErr
+    send_all(request->sd, (const char *)&BadParamErr, sizeof(BadParamErr));
+}
+
+#ifdef APPLE_OSX_mDNSResponder
+// The caller can specify either the pid or the uuid. If the pid is not specified,
+// update the effective uuid. Don't overwrite the pid which is used for debugging
+// purposes and initialized when the socket is opened.
+mDNSlocal void handle_connection_delegate_request(request_state *request)
+{
+    mDNSs32 pid;
+    socklen_t len;
+
+    len = 0;
+    pid = get_uint32(&request->msgptr, request->msgend);
+#ifdef LOCAL_PEEREPID
+    if (pid)
+    {
+        len = sizeof(pid);
+        if (getsockopt(request->sd, SOL_LOCAL, LOCAL_PEEREPID, &request->process_id, &len) != 0)
+            return;
+        // to extract the process name from the pid value
+        if (proc_pidinfo(request->process_id, PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0)
+            return;
+        mDNSPlatformStrCopy(request->pid_name, proc.pbsi_comm);
+        //LogMsg("handle_connection_delegate_request: process id %d, name %s", request->process_id, request->pid_name);
+    }
+#endif
+#ifdef LOCAL_PEEREUUID
+    if (!pid)
+    {
+        len = UUID_SIZE;
+        if (getsockopt(request->sd, SOL_LOCAL, LOCAL_PEEREUUID, request->uuid, &len) != 0)
+            return;
+        request->validUUID = mDNStrue;
+    }
+#endif
+}
+#else
+mDNSlocal void handle_connection_delegate_request(request_state *request)
+{
+    (void) request;
+}
+#endif
+
+typedef packedstruct
+{
+    mStatus err;
+    mDNSs32 pid;
+} PIDInfo;
+
+mDNSlocal void handle_getpid_request(request_state *request)
+{
+    const request_state *req;
+    mDNSs32 pid = -1;
+    mDNSu16 srcport = get_uint16(&request->msgptr, request->msgend);
+    const DNSQuestion *q = NULL;
+    PIDInfo pi;
+
+    LogOperation("%3d: DNSServiceGetPID START", request->sd);
+
+    for (req = all_requests; req; req=req->next)
+    {
+        if (req->hdr.op == query_request)
+            q = &req->u.queryrecord.q;
+        else if (req->hdr.op == addrinfo_request)
+            q = &req->u.addrinfo.q4;
+        else if (req->hdr.op == addrinfo_request)
+            q = &req->u.addrinfo.q6;
+
+        if (q && q->LocalSocket != NULL)
+        {
+            mDNSu16 port = mDNSPlatformGetUDPPort(q->LocalSocket);
+            if (port == srcport)
+            {
+                pid = req->process_id;
+                LogInfo("DNSServiceGetPID: srcport %d, pid %d [%s] question %##s", htons(srcport), pid, req->pid_name, q->qname.c);
+                break;
+            }
+        }
+    }
+    // If we cannot find in the client requests, look to see if this was
+    // started by mDNSResponder.
+    if (pid == -1)
+    {
+        for (q = mDNSStorage.Questions; q; q = q->next)
         {
         {
-            DaemonVersionReply x = { 0, dnssd_htonl(4), dnssd_htonl(_DNS_SD_H) };
-            send_all(request->sd, (const char *)&x, sizeof(x));
-            return;
+            if (q && q->LocalSocket != NULL)
+            {
+                mDNSu16 port = mDNSPlatformGetUDPPort(q->LocalSocket);
+                if (port == srcport)
+                {
+#if APPLE_OSX_mDNSResponder
+                    pid = getpid();
+#endif // APPLE_OSX_mDNSResponder
+                    LogInfo("DNSServiceGetPID: srcport %d, pid %d [%s], question %##s", htons(srcport), pid, "_mDNSResponder", q->qname.c);
+                    break;
+                }
+            }
         }
     }
         }
     }
-
-    // If we didn't recogize the requested property name, return BadParamErr
-    send_all(request->sd, (const char *)&BadParamErr, sizeof(BadParamErr));
+        
+    pi.err = 0;
+    pi.pid = pid;
+    send_all(request->sd, (const char *)&pi, sizeof(PIDInfo));
+    LogOperation("%3d: DNSServiceGetPID STOP", request->sd);
 }
 
 // ***************************************************************************
 }
 
 // ***************************************************************************
@@ -3439,11 +4091,11 @@ mDNSlocal void port_mapping_termination_callback(request_state *request)
     LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) STOP PID[%d](%s)", request->sd,
                  DNSServiceProtocol(request->u.pm.NATinfo.Protocol),
                  mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease, 
     LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) STOP PID[%d](%s)", request->sd,
                  DNSServiceProtocol(request->u.pm.NATinfo.Protocol),
                  mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease, 
-                get_peer_pid(request->sd, pid_name), pid_name);
+                        request->process_id, request->pid_name);
     mDNS_StopNATOperation(&mDNSStorage, &request->u.pm.NATinfo);
 }
 
     mDNS_StopNATOperation(&mDNSStorage, &request->u.pm.NATinfo);
 }
 
-// Called via function pointer when we get a NAT-PMP address request or port mapping response
+// Called via function pointer when we get a NAT Traversal (address request or port mapping) response
 mDNSlocal void port_mapping_create_request_callback(mDNS *m, NATTraversalInfo *n)
 {
     request_state *request = (request_state *)n->clientContext;
 mDNSlocal void port_mapping_create_request_callback(mDNS *m, NATTraversalInfo *n)
 {
     request_state *request = (request_state *)n->clientContext;
@@ -3530,7 +4182,7 @@ mDNSlocal mStatus handle_port_mapping_request(request_state *request)
 
     LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) START PID[%d](%s)", request->sd,
                  protocol, mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease, 
 
     LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) START PID[%d](%s)", request->sd,
                  protocol, mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease, 
-                get_peer_pid(request->sd, pid_name), pid_name);
+                        request->process_id, request->pid_name);
     err = mDNS_StartNATOperation(&mDNSStorage, &request->u.pm.NATinfo);
     if (err) LogMsg("ERROR: mDNS_StartNATOperation: %d", (int)err);
     else request->terminate = port_mapping_termination_callback;
     err = mDNS_StartNATOperation(&mDNSStorage, &request->u.pm.NATinfo);
     if (err) LogMsg("ERROR: mDNS_StartNATOperation: %d", (int)err);
     else request->terminate = port_mapping_termination_callback;
@@ -3547,11 +4199,12 @@ mDNSlocal mStatus handle_port_mapping_request(request_state *request)
 mDNSlocal void addrinfo_termination_callback(request_state *request)
 {
     LogOperation("%3d: DNSServiceGetAddrInfo(%##s) STOP PID[%d](%s)", request->sd, request->u.addrinfo.q4.qname.c,
 mDNSlocal void addrinfo_termination_callback(request_state *request)
 {
     LogOperation("%3d: DNSServiceGetAddrInfo(%##s) STOP PID[%d](%s)", request->sd, request->u.addrinfo.q4.qname.c,
-               get_peer_pid(request->sd, pid_name), pid_name);
+                         request->process_id, request->pid_name);
 
     if (request->u.addrinfo.q4.QuestionContext)
     {
         mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q4);
 
     if (request->u.addrinfo.q4.QuestionContext)
     {
         mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q4);
+        LogMcastQ(&mDNSStorage, &request->u.addrinfo.q4, request, q_stop);
         request->u.addrinfo.q4.QuestionContext = mDNSNULL;
     }
     if (request->u.addrinfo.q4.qnameOrig)
         request->u.addrinfo.q4.QuestionContext = mDNSNULL;
     }
     if (request->u.addrinfo.q4.qnameOrig)
@@ -3565,6 +4218,7 @@ mDNSlocal void addrinfo_termination_callback(request_state *request)
         {
             LogInfo("addrinfo_termination_callback: Stopping q42 %##s", request->u.addrinfo.q42->qname.c);
             mDNS_StopQuery(&mDNSStorage, request->u.addrinfo.q42);
         {
             LogInfo("addrinfo_termination_callback: Stopping q42 %##s", request->u.addrinfo.q42->qname.c);
             mDNS_StopQuery(&mDNSStorage, request->u.addrinfo.q42);
+            LogMcastQ(&mDNSStorage, request->u.addrinfo.q42, request, q_stop);
         }
         if (request->u.addrinfo.q42->qnameOrig)
         {
         }
         if (request->u.addrinfo.q42->qnameOrig)
         {
@@ -3579,6 +4233,7 @@ mDNSlocal void addrinfo_termination_callback(request_state *request)
     if (request->u.addrinfo.q6.QuestionContext)
     {
         mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q6);
     if (request->u.addrinfo.q6.QuestionContext)
     {
         mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q6);
+        LogMcastQ(&mDNSStorage, &request->u.addrinfo.q6, request, q_stop);
         request->u.addrinfo.q6.QuestionContext = mDNSNULL;
     }
     if (request->u.addrinfo.q6.qnameOrig)
         request->u.addrinfo.q6.QuestionContext = mDNSNULL;
     }
     if (request->u.addrinfo.q6.qnameOrig)
@@ -3592,6 +4247,7 @@ mDNSlocal void addrinfo_termination_callback(request_state *request)
         {
             LogInfo("addrinfo_termination_callback: Stopping q62 %##s", request->u.addrinfo.q62->qname.c);
             mDNS_StopQuery(&mDNSStorage, request->u.addrinfo.q62);
         {
             LogInfo("addrinfo_termination_callback: Stopping q62 %##s", request->u.addrinfo.q62->qname.c);
             mDNS_StopQuery(&mDNSStorage, request->u.addrinfo.q62);
+            LogMcastQ(&mDNSStorage, request->u.addrinfo.q62, request, q_stop);
         }
         if (request->u.addrinfo.q62->qnameOrig)
         {
         }
         if (request->u.addrinfo.q62->qnameOrig)
         {
@@ -3602,6 +4258,36 @@ mDNSlocal void addrinfo_termination_callback(request_state *request)
         freeL("addrinfo Q62", request->u.addrinfo.q62);
         request->u.addrinfo.q62 = mDNSNULL;
     }
         freeL("addrinfo Q62", request->u.addrinfo.q62);
         request->u.addrinfo.q62 = mDNSNULL;
     }
+#if APPLE_OSX_mDNSResponder
+    {
+        DNSQuestion *v4q, *v6q;
+        v4q = v6q = mDNSNULL;
+        if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4)
+        {
+            // If we are not delivering answers, we may be timing out prematurely.
+            // Note down the current state so that we know to retry when we see a
+            // valid response again.
+            if (request->u.addrinfo.q4.TimeoutQuestion && !request->u.addrinfo.v4ans)
+            {
+                mDNSPlatformUpdateDNSStatus(&mDNSStorage, &request->u.addrinfo.q4);
+            }
+            // If we have a v4 answer and if we timed out prematurely before, provide
+            // a trigger to the upper layer so that it can retry questions if needed.
+            if (request->u.addrinfo.v4ans)
+                v4q = &request->u.addrinfo.q4;
+        }
+        if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6)
+        {
+            if (request->u.addrinfo.q6.TimeoutQuestion && !request->u.addrinfo.v6ans)
+            {
+                mDNSPlatformUpdateDNSStatus(&mDNSStorage, &request->u.addrinfo.q6);
+            }
+            if (request->u.addrinfo.v6ans)
+                v6q = &request->u.addrinfo.q6;
+        }
+        mDNSPlatformTriggerDNSRetry(&mDNSStorage, v4q, v6q);
+    }
+#endif // APPLE_OSX_mDNSResponder
 }
 
 mDNSlocal mStatus handle_addrinfo_request(request_state *request)
 }
 
 mDNSlocal mStatus handle_addrinfo_request(request_state *request)
@@ -3650,48 +4336,33 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request)
     request->u.addrinfo.q4.SuppressUnusable = request->u.addrinfo.q6.SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable   ) != 0;
     request->u.addrinfo.q4.TimeoutQuestion  = request->u.addrinfo.q6.TimeoutQuestion  = (flags & kDNSServiceFlagsTimeout            ) != 0;
     request->u.addrinfo.q4.WakeOnResolve    = request->u.addrinfo.q6.WakeOnResolve    = 0;
     request->u.addrinfo.q4.SuppressUnusable = request->u.addrinfo.q6.SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable   ) != 0;
     request->u.addrinfo.q4.TimeoutQuestion  = request->u.addrinfo.q6.TimeoutQuestion  = (flags & kDNSServiceFlagsTimeout            ) != 0;
     request->u.addrinfo.q4.WakeOnResolve    = request->u.addrinfo.q6.WakeOnResolve    = 0;
-    request->u.addrinfo.q4.UseBrackgroundTrafficClass = request->u.addrinfo.q6.UseBrackgroundTrafficClass  = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0;
-    request->u.addrinfo.q4.ValidationRequired = request->u.addrinfo.q6.ValidationRequired = 0;
+    request->u.addrinfo.q4.UseBackgroundTrafficClass = request->u.addrinfo.q6.UseBackgroundTrafficClass  = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0;
+    if ((flags & kDNSServiceFlagsValidate) != 0)
+        request->u.addrinfo.q4.ValidationRequired = request->u.addrinfo.q6.ValidationRequired = DNSSEC_VALIDATION_SECURE;
+    else if ((flags & kDNSServiceFlagsValidateOptional) != 0)
+        request->u.addrinfo.q4.ValidationRequired = request->u.addrinfo.q6.ValidationRequired = DNSSEC_VALIDATION_SECURE_OPTIONAL;
     request->u.addrinfo.q4.ValidatingResponse = request->u.addrinfo.q6.ValidatingResponse = 0;
     request->u.addrinfo.q4.ValidatingResponse = request->u.addrinfo.q6.ValidatingResponse = 0;
-    request->u.addrinfo.q4.qnameOrig        = request->u.addrinfo.q6.qnameOrig        = mDNSNULL;
+    request->u.addrinfo.q4.ProxyQuestion      = request->u.addrinfo.q6.ProxyQuestion      = 0;
+    request->u.addrinfo.q4.qnameOrig          = request->u.addrinfo.q6.qnameOrig          = mDNSNULL;
+    request->u.addrinfo.q4.AnonInfo           = request->u.addrinfo.q6.AnonInfo           = mDNSNULL;
 
 
-    if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4)
-    {
-        request->u.addrinfo.q4.qtype            = kDNSServiceType_A;
-        request->u.addrinfo.q4.SearchListIndex  = 0;
+    SetQuestionPolicy(&request->u.addrinfo.q4, request);
+    SetQuestionPolicy(&request->u.addrinfo.q6, request);
 
 
-        // 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);
-        if (err != mStatus_NoError)
-        {
-            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
-    }
+    request->u.addrinfo.q4.DNSSECAuthInfo = request->u.addrinfo.q6.DNSSECAuthInfo = mDNSNULL;
+    request->u.addrinfo.q4.DAIFreeCallback = request->u.addrinfo.q6.DAIFreeCallback = mDNSNULL;
+
+    //Turn off dnssec validation for local domains
+    if (IsLocalDomain(&d))
+        request->u.addrinfo.q4.ValidationRequired = request->u.addrinfo.q6.ValidationRequired = 0;
 
 
-    if (!err && (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6))
+    if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6)
     {
         request->u.addrinfo.q6.qtype            = kDNSServiceType_AAAA;
         request->u.addrinfo.q6.SearchListIndex  = 0;
     {
         request->u.addrinfo.q6.qtype            = kDNSServiceType_AAAA;
         request->u.addrinfo.q6.SearchListIndex  = 0;
-        if (hostname[strlen(hostname) - 1] != '.' && (AlwaysAppendSearchDomains || CountLabels(&d) == 1))
+        // For DNSSEC questions, append search domains only if kDNSServiceFlagsValidateOptional is set
+        if ((!(request->u.addrinfo.q6.ValidationRequired == DNSSEC_VALIDATION_SECURE)) && (!(request->u.addrinfo.q6.ValidationRequired == DNSSEC_VALIDATION_INSECURE)) 
+            && hostname[strlen(hostname) - 1] != '.' && (AlwaysAppendSearchDomains || CountLabels(&d) == 1))
         {
             request->u.addrinfo.q6.AppendSearchDomains = 1;
             request->u.addrinfo.q6.AppendLocalSearchDomains = 1;
         {
             request->u.addrinfo.q6.AppendSearchDomains = 1;
             request->u.addrinfo.q6.AppendLocalSearchDomains = 1;
@@ -3709,23 +4380,64 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request)
         {
             LogMsg("ERROR: mDNS_StartQuery: %d", (int)err);
             request->u.addrinfo.q6.QuestionContext = mDNSNULL;
         {
             LogMsg("ERROR: mDNS_StartQuery: %d", (int)err);
             request->u.addrinfo.q6.QuestionContext = mDNSNULL;
-            if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4)
-            {
-                // If we started a query for IPv4, we need to cancel it
-                mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q4);
-                request->u.addrinfo.q4.QuestionContext = mDNSNULL;
-            }
         }
         #if APPLE_OSX_mDNSResponder
         err = SendAdditionalQuery(&request->u.addrinfo.q6, request, err);
         #endif // APPLE_OSX_mDNSResponder
         }
         #if APPLE_OSX_mDNSResponder
         err = SendAdditionalQuery(&request->u.addrinfo.q6, request, err);
         #endif // APPLE_OSX_mDNSResponder
+        if (!err)
+        { 
+            request->terminate = addrinfo_termination_callback;
+            LogMcastQ(&mDNSStorage, &request->u.addrinfo.q6, request, q_start);
+        }
     }
 
     }
 
-    LogOperation("%3d: DNSServiceGetAddrInfo(%X, %d, %d, %##s) START PID[%d](%s)",
-                 request->sd, flags, interfaceIndex, request->u.addrinfo.protocol, d.c, get_peer_pid(request->sd, pid_name), pid_name);
+    if (!err && (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 cmd line arg
+        // "AlwaysAppendSearchDomains", then we do it for any query which is not fully qualified.
+        // For DNSSEC questions, append search domains only if kDNSServiceFlagsValidateOptional is set.
 
 
-    if (!err) request->terminate = addrinfo_termination_callback;
+        if ((!(request->u.addrinfo.q4.ValidationRequired == DNSSEC_VALIDATION_SECURE)) && (!(request->u.addrinfo.q4.ValidationRequired == DNSSEC_VALIDATION_INSECURE)) 
+            && 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);
+        if (err != mStatus_NoError)
+        {
+            LogMsg("ERROR: mDNS_StartQuery: %d", (int)err);
+            request->u.addrinfo.q4.QuestionContext = mDNSNULL;
+            if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6)
+            {    
+                // If we started a query for IPv6, we need to cancel it                                                                                                           
+                mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q6);
+                request->u.addrinfo.q6.QuestionContext = mDNSNULL;
+            }    
+        }
+        #if APPLE_OSX_mDNSResponder
+        err = SendAdditionalQuery(&request->u.addrinfo.q4, request, err);
+        #endif // APPLE_OSX_mDNSResponder
+        if (!err)
+        {
+            request->terminate = addrinfo_termination_callback;
+            LogMcastQ(&mDNSStorage, &request->u.addrinfo.q4, request, q_start);
+        }        
+    }
 
 
+    LogOperation("%3d: DNSServiceGetAddrInfo(%X, %d, %d, %##s) START PID[%d](%s)", request->sd, flags, interfaceIndex, 
+                 request->u.addrinfo.protocol, d.c, request->process_id, request->pid_name);
     return(err);
 }
 
     return(err);
 }
 
@@ -3738,9 +4450,11 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request)
 mDNSlocal request_state *NewRequest(void)
 {
     request_state **p = &all_requests;
 mDNSlocal request_state *NewRequest(void)
 {
     request_state **p = &all_requests;
-    while (*p) p=&(*p)->next;
+    while (*p) 
+        p=&(*p)->next;
     *p = mallocL("request_state", sizeof(request_state));
     *p = mallocL("request_state", sizeof(request_state));
-    if (!*p) FatalError("ERROR: malloc");
+    if (!*p) 
+        FatalError("ERROR: malloc");
     mDNSPlatformMemZero(*p, sizeof(request_state));
     return(*p);
 }
     mDNSPlatformMemZero(*p, sizeof(request_state));
     return(*p);
 }
@@ -3808,7 +4522,7 @@ mDNSlocal void read_msg(request_state *req)
         struct iovec vec = { req->msgbuf + req->data_bytes, nleft };    // Tell recvmsg where we want the bytes put
         struct msghdr msg;
         struct cmsghdr *cmsg;
         struct iovec vec = { req->msgbuf + req->data_bytes, nleft };    // Tell recvmsg where we want the bytes put
         struct msghdr msg;
         struct cmsghdr *cmsg;
-        char cbuf[CMSG_SPACE(sizeof(dnssd_sock_t))];
+        char cbuf[CMSG_SPACE(4 * sizeof(dnssd_sock_t))];
         msg.msg_name       = 0;
         msg.msg_namelen    = 0;
         msg.msg_iov        = &vec;
         msg.msg_name       = 0;
         msg.msg_namelen    = 0;
         msg.msg_iov        = &vec;
@@ -3831,9 +4545,8 @@ mDNSlocal void read_msg(request_state *req)
         LogMsg("%3d: Expecting %d %d %d %d", req->sd, sizeof(cbuf),       sizeof(cbuf),   SOL_SOCKET,       SCM_RIGHTS);
         LogMsg("%3d: Got       %d %d %d %d", req->sd, msg.msg_controllen, cmsg->cmsg_len, cmsg->cmsg_level, cmsg->cmsg_type);
 #endif // DEBUG_64BIT_SCM_RIGHTS
         LogMsg("%3d: Expecting %d %d %d %d", req->sd, sizeof(cbuf),       sizeof(cbuf),   SOL_SOCKET,       SCM_RIGHTS);
         LogMsg("%3d: Got       %d %d %d %d", req->sd, msg.msg_controllen, cmsg->cmsg_len, cmsg->cmsg_level, cmsg->cmsg_type);
 #endif // DEBUG_64BIT_SCM_RIGHTS
-        if (msg.msg_controllen == sizeof(cbuf) &&
-            cmsg->cmsg_len     == CMSG_LEN(sizeof(dnssd_sock_t)) &&
-            cmsg->cmsg_level   == SOL_SOCKET   &&
+        if (msg.msg_controllen != 0 &&
+            cmsg->cmsg_level   == SOL_SOCKET &&
             cmsg->cmsg_type    == SCM_RIGHTS)
         {
 #if APPLE_OSX_mDNSResponder
             cmsg->cmsg_type    == SCM_RIGHTS)
         {
 #if APPLE_OSX_mDNSResponder
@@ -3843,7 +4556,7 @@ mDNSlocal void read_msg(request_state *req)
             if (req->hdr.op == send_bpf)
             {
                 dnssd_sock_t x = *(dnssd_sock_t *)CMSG_DATA(cmsg);
             if (req->hdr.op == send_bpf)
             {
                 dnssd_sock_t x = *(dnssd_sock_t *)CMSG_DATA(cmsg);
-                LogOperation("%3d: Got BPF %d", req->sd, x);
+                LogOperation("%3d: Got len %d, BPF %d", req->sd, cmsg->cmsg_len, x);
                 mDNSPlatformReceiveBPF_fd(&mDNSStorage, x);
             }
             else
                 mDNSPlatformReceiveBPF_fd(&mDNSStorage, x);
             }
             else
@@ -3855,7 +4568,7 @@ mDNSlocal void read_msg(request_state *req)
             if (req->data_bytes < req->hdr.datalen)
             {
                 LogMsg("%3d: Client(PID [%d](%s)) sent error socket %d via SCM_RIGHTS with req->data_bytes %d < req->hdr.datalen %d",
             if (req->data_bytes < req->hdr.datalen)
             {
                 LogMsg("%3d: Client(PID [%d](%s)) sent error socket %d via SCM_RIGHTS with req->data_bytes %d < req->hdr.datalen %d",
-                       req->sd, get_peer_pid(req->sd, pid_name), pid_name, req->errsd, req->data_bytes, req->hdr.datalen);
+                       req->sd, req->process_id, req->pid_name, req->errsd, req->data_bytes, req->hdr.datalen);
                 req->ts = t_error;
                 return;
             }
                 req->ts = t_error;
                 return;
             }
@@ -3963,45 +4676,68 @@ mDNSlocal void request_callback(int fd, short filter, void *info)
     for (;;)
     {
         read_msg(req);
     for (;;)
     {
         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->ts == t_morecoming) 
+            return;
+        if (req->ts == t_terminated || req->ts == t_error) 
+        { 
+            AbortUnlinkAndFree(req); 
+            return; 
+        }
+        if (req->ts != t_complete) 
+        { 
+            LogMsg("request_callback: req->ts %d != t_complete PID[%d][%s]", req->ts, req->process_id, req->pid_name); 
+            AbortUnlinkAndFree(req); 
+            return; 
+        }
         if (req->hdr.version != VERSION)
         {
         if (req->hdr.version != VERSION)
         {
-            LogMsg("ERROR: client version %d incompatible with daemon version %d", req->hdr.version, VERSION);
+            LogMsg("request_callback: ERROR: client IPC version %d incompatible with daemon IPC version %d PID[%d][%s]", 
+                   req->hdr.version, VERSION, req->process_id, req->pid_name);
             AbortUnlinkAndFree(req);
             return;
         }
 
         switch(req->hdr.op)            //          Interface       + other data
         {
             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;
-        case release_request:          min_size += sizeof(mDNSu32) + 3 /* type, type, domain */;                               break;
-        default: LogMsg("ERROR: validate_message - unsupported req type: %d", req->hdr.op); min_size = -1;                     break;
+            case connection_request:       min_size = 0;                                                                           break;
+            case connection_delegate_request: min_size = 4; /* pid */                                                              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 getpid_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;
+            case release_request:          min_size += sizeof(mDNSu32) + 3 /* type, type, domain */;                               break;
+            default: LogMsg("request_callback: ERROR: validate_message - unsupported req type: %d PID[%d][%s]", 
+                            req->hdr.op, req->process_id, req->pid_name); 
+                     min_size = -1;                                                                                                break;
         }
 
         if ((mDNSs32)req->data_bytes < min_size)
         }
 
         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; }
-
+        { 
+            LogMsg("request_callback: Invalid message %d bytes; min for %d is %d PID[%d][%s]", 
+                    req->data_bytes, req->hdr.op, min_size, req->process_id, req->pid_name); 
+            AbortUnlinkAndFree(req); 
+            return; 
+        }
         if (LightweightOp(req->hdr.op) && !req->terminate)
         if (LightweightOp(req->hdr.op) && !req->terminate)
-        { LogMsg("Reg/Add/Update/Remove %d require existing connection", req->hdr.op);                  AbortUnlinkAndFree(req); return; }
+        { 
+            LogMsg("request_callback: Reg/Add/Update/Remove %d require existing connection PID[%d][%s]", 
+                    req->hdr.op, req->process_id, req->pid_name);                  
+            AbortUnlinkAndFree(req); 
+            return; 
+        }
 
         // check if client wants silent operation
         if (req->hdr.ipc_flags & IPC_FLAGS_NOREPLY) req->no_reply = 1;
 
         // check if client wants silent operation
         if (req->hdr.ipc_flags & IPC_FLAGS_NOREPLY) req->no_reply = 1;
@@ -4018,6 +4754,28 @@ mDNSlocal void request_callback(int fd, short filter, void *info)
             newreq->msgbuf  = req->msgbuf;
             newreq->msgptr  = req->msgptr;
             newreq->msgend  = req->msgend;
             newreq->msgbuf  = req->msgbuf;
             newreq->msgptr  = req->msgptr;
             newreq->msgend  = req->msgend;
+            // if the parent request is a delegate connection, copy the
+            // relevant bits
+            if (req->validUUID)
+            {
+                int i;
+                newreq->validUUID = mDNStrue;
+                for (i = 0; i < UUID_SIZE; i++)
+                {
+                    newreq->uuid[i] = req->uuid[i];
+                }
+            }
+            else
+            {
+                if (req->process_id)
+                {
+                    newreq->process_id = req->process_id;
+                }
+                else
+                {
+                    set_peer_pid(newreq);
+                }
+            }
             req = newreq;
         }
 
             req = newreq;
         }
 
@@ -4027,40 +4785,52 @@ mDNSlocal void request_callback(int fd, short filter, void *info)
         {
             err = mStatus_ServiceNotRunning;
         }
         {
             err = mStatus_ServiceNotRunning;
         }
-        else switch(req->hdr.op)
+        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 PID[%d](%s)", 
-                                                       req->sd, get_peer_pid(req->sd, pid_name), pid_name);
-                                              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;
-            case release_request:              err = handle_release_request     (req);  break;
-            default: LogMsg("%3d: ERROR: Unsupported UDS req: %d", req->sd, req->hdr.op);
+                // These are all operations that have their own first-class request_state object
+                case connection_request:
+                            LogOperation("%3d: DNSServiceCreateConnection START PID[%d](%s)",
+                                req->sd, req->process_id, req->pid_name);
+                            req->terminate = connection_termination;
+                            break;
+                case connection_delegate_request:
+                            LogOperation("%3d: DNSServiceCreateDelegateConnection START PID[%d](%s)",
+                                req->sd, req->process_id, req->pid_name);
+                            req->terminate = connection_termination;
+                            handle_connection_delegate_request(req);
+                            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 getpid_request:                     handle_getpid_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;
+                case release_request:              err = handle_release_request     (req);  break;
+                default: LogMsg("request_callback: %3d:ERROR: Unsupported UDS req:%d PID[%d][%s]", 
+                                req->sd, req->hdr.op, req->process_id, req->pid_name);      break;
             }
             }
-
+        }
         // 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
         // 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)
+        if (req->hdr.op != cancel_request && req->hdr.op != getproperty_request && req->hdr.op != send_bpf && req->hdr.op != getpid_request)
         {
             const mStatus err_netorder = dnssd_htonl(err);
             send_all(req->errsd, (const char *)&err_netorder, sizeof(err_netorder));
         {
             const mStatus err_netorder = dnssd_htonl(err);
             send_all(req->errsd, (const char *)&err_netorder, sizeof(err_netorder));
@@ -4127,6 +4897,7 @@ mDNSlocal void connect_callback(int fd, short filter, void *info)
         request->ts    = t_morecoming;
         request->sd    = sd;
         request->errsd = sd;
         request->ts    = t_morecoming;
         request->sd    = sd;
         request->errsd = sd;
+        set_peer_pid(request);
 #if APPLE_OSX_mDNSResponder
         struct xucred x;
         socklen_t xucredlen = sizeof(x);
 #if APPLE_OSX_mDNSResponder
         struct xucred x;
         socklen_t xucredlen = sizeof(x);
@@ -4173,8 +4944,11 @@ mDNSlocal mDNSBool uds_socket_setup(dnssd_sock_t skt)
         my_perror("ERROR: could not add listen socket to event loop");
         return mDNSfalse;
     }
         my_perror("ERROR: could not add listen socket to event loop");
         return mDNSfalse;
     }
-    else LogOperation("%3d: Listening for incoming Unix Domain Socket client requests", skt);
-
+    else
+    { 
+        LogMsg("%3d: Listening for incoming Unix Domain Socket client requests", skt);
+        mDNSStorage.uds_listener_skt = skt;
+    }
     return mDNStrue;
 }
 
     return mDNStrue;
 }
 
@@ -4184,7 +4958,7 @@ mDNSexport int udsserver_init(dnssd_sock_t skts[], mDNSu32 count)
     int ret;
     mDNSu32 i = 0;
 
     int ret;
     mDNSu32 i = 0;
 
-    LogInfo("udsserver_init");
+    LogInfo("udsserver_init: %d %d", _DNS_SD_H/10000, mDNSStorage.mDNS_plat);
 
     // If a particular platform wants to opt out of having a PID file, define PID_FILE to be ""
     if (PID_FILE[0])
 
     // If a particular platform wants to opt out of having a PID file, define PID_FILE to be ""
     if (PID_FILE[0])
@@ -4331,13 +5105,13 @@ mDNSexport int udsserver_exit(void)
     return 0;
 }
 
     return 0;
 }
 
-mDNSlocal void LogClientInfo(mDNS *const m, const request_state *req)
+mDNSlocal void LogClientInfo(mDNS *const m, request_state *req)
 {
     char prefix[16];
 {
     char prefix[16];
-    if (req->primary) mDNS_snprintf(prefix, sizeof(prefix), " -> ");
-    else mDNS_snprintf(prefix, sizeof(prefix), "%3d:", req->sd);
-
-    usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
+    if (req->primary) 
+        mDNS_snprintf(prefix, sizeof(prefix), " -> ");
+    else 
+        mDNS_snprintf(prefix, sizeof(prefix), "%3d:", req->sd);
 
     if (!req->terminate)
         LogMsgNoIdent("%s No operation yet on this socket", prefix);
 
     if (!req->terminate)
         LogMsgNoIdent("%s No operation yet on this socket", prefix);
@@ -4345,59 +5119,198 @@ mDNSlocal void LogClientInfo(mDNS *const m, const request_state *req)
     {
         int num_records = 0, num_ops = 0;
         const registered_record_entry *p;
     {
         int num_records = 0, num_ops = 0;
         const registered_record_entry *p;
-        const request_state *r;
+        request_state *r;
         for (p = req->u.reg_recs; p; p=p->next) num_records++;
         for (r = req->next; r; r=r->next) if (r->primary == req) num_ops++;
         LogMsgNoIdent("%s DNSServiceCreateConnection: %d registered record%s, %d kDNSServiceFlagsShareConnection operation%s PID[%d](%s)", 
         for (p = req->u.reg_recs; p; p=p->next) num_records++;
         for (r = req->next; r; r=r->next) if (r->primary == req) num_ops++;
         LogMsgNoIdent("%s DNSServiceCreateConnection: %d registered record%s, %d kDNSServiceFlagsShareConnection operation%s PID[%d](%s)", 
-               prefix, num_records, num_records != 1 ? "s" : "", num_ops,     num_ops     != 1 ? "s" : "", get_peer_pid(req->sd, pid_name), pid_name);
+                              prefix, num_records, num_records != 1 ? "s" : "", num_ops,     num_ops     != 1 ? "s" : "", 
+                       req->process_id, req->pid_name);
         for (p = req->u.reg_recs; p; p=p->next)
         for (p = req->u.reg_recs; p; p=p->next)
-            LogMsgNoIdent(" ->  DNSServiceRegisterRecord %3d %s PID[%d](%s)", p->key, ARDisplayString(m, p->rr), get_peer_pid(req->sd, pid_name), pid_name);
+            LogMsgNoIdent(" ->  DNSServiceRegisterRecord %3d %s PID[%d](%s)", p->key, ARDisplayString(m, p->rr), 
+                          req->process_id, req->pid_name);
         for (r = req->next; r; r=r->next) if (r->primary == req) LogClientInfo(m, r);
     }
     else if (req->terminate == regservice_termination_callback)
     {
         service_instance *ptr;
         for (r = req->next; r; r=r->next) if (r->primary == req) LogClientInfo(m, r);
     }
     else if (req->terminate == regservice_termination_callback)
     {
         service_instance *ptr;
+        char anonstr[256];
         for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next)
         for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next)
-            LogMsgNoIdent("%s DNSServiceRegister         %##s %u/%u PID[%d](%s)",
-                          (ptr == req->u.servicereg.instances) ? prefix : "    ", ptr->srs.RR_SRV.resrec.name->c,
-                          mDNSVal16(req->u.servicereg.port), SRS_PORT(&ptr->srs), get_peer_pid(req->sd, pid_name), pid_name);
+            LogMsgNoIdent("%s DNSServiceRegister         %##s%s %u/%u PID[%d](%s)",
+                    (ptr == req->u.servicereg.instances) ? prefix : "    ", ptr->srs.RR_SRV.resrec.name->c,
+                    AnonDataToString(ptr->srs.AnonData, 0, anonstr, sizeof(anonstr)), mDNSVal16(req->u.servicereg.port),
+                    SRS_PORT(&ptr->srs), req->process_id, req->pid_name);
     }
     else if (req->terminate == browse_termination_callback)
     {
         browser_t *blist;
     }
     else if (req->terminate == browse_termination_callback)
     {
         browser_t *blist;
+        char anonstr[256];
         for (blist = req->u.browser.browsers; blist; blist = blist->next)
         for (blist = req->u.browser.browsers; blist; blist = blist->next)
-            LogMsgNoIdent("%s DNSServiceBrowse           %##s PID[%d](%s)", 
-               (blist == req->u.browser.browsers) ? prefix : "    ",blist->q.qname.c, get_peer_pid(req->sd, pid_name), pid_name);
+            LogMsgNoIdent("%s DNSServiceBrowse           %##s%s PID[%d](%s)", 
+                   (blist == req->u.browser.browsers) ? prefix : "    ",blist->q.qname.c,
+            AnonDataToString(req->u.browser.AnonData, 0, anonstr, sizeof(anonstr)), req->process_id, req->pid_name);
     }
     else if (req->terminate == resolve_termination_callback)
         LogMsgNoIdent("%s DNSServiceResolve          %##s PID[%d](%s)", 
     }
     else if (req->terminate == resolve_termination_callback)
         LogMsgNoIdent("%s DNSServiceResolve          %##s PID[%d](%s)", 
-               prefix, req->u.resolve.qsrv.qname.c, get_peer_pid(req->sd, pid_name), pid_name);
+               prefix, req->u.resolve.qsrv.qname.c, req->process_id, req->pid_name);
     else if (req->terminate == queryrecord_termination_callback)
         LogMsgNoIdent("%s DNSServiceQueryRecord      %##s (%s) PID[%d](%s)", 
     else if (req->terminate == queryrecord_termination_callback)
         LogMsgNoIdent("%s DNSServiceQueryRecord      %##s (%s) PID[%d](%s)", 
-               prefix, req->u.queryrecord.q.qname.c, DNSTypeName(req->u.queryrecord.q.qtype), get_peer_pid(req->sd, pid_name), pid_name);
+               prefix, req->u.queryrecord.q.qname.c, DNSTypeName(req->u.queryrecord.q.qtype), req->process_id, req->pid_name);
     else if (req->terminate == enum_termination_callback)
     else if (req->terminate == enum_termination_callback)
-        LogMsgNoIdent("%s DNSServiceEnumerateDomains %##s PID[%d](%s)", prefix, req->u.enumeration.q_all.qname.c, get_peer_pid(req->sd, pid_name), pid_name);
+        LogMsgNoIdent("%s DNSServiceEnumerateDomains %##s PID[%d](%s)", prefix, req->u.enumeration.q_all.qname.c, 
+                       req->process_id, req->pid_name);
     else if (req->terminate == port_mapping_termination_callback)
     else if (req->terminate == port_mapping_termination_callback)
-        LogMsgNoIdent("%s DNSServiceNATPortMapping   %.4a %s%s Int %d Req %d Ext %d Req TTL %d Granted TTL %d PID[%d](%s)",
+        LogMsgNoIdent("%s DNSServiceNATPortMapping   %s%s Int %5d Req %5d Ext %.4a:%5d Req TTL %5d Granted TTL %5d PID[%d](%s)",
                       prefix,
                       prefix,
-                      &req->u.pm.NATinfo.ExternalAddress,
                       req->u.pm.NATinfo.Protocol & NATOp_MapTCP ? "TCP" : "   ",
                       req->u.pm.NATinfo.Protocol & NATOp_MapUDP ? "UDP" : "   ",
                       mDNSVal16(req->u.pm.NATinfo.IntPort),
                       mDNSVal16(req->u.pm.ReqExt),
                       req->u.pm.NATinfo.Protocol & NATOp_MapTCP ? "TCP" : "   ",
                       req->u.pm.NATinfo.Protocol & NATOp_MapUDP ? "UDP" : "   ",
                       mDNSVal16(req->u.pm.NATinfo.IntPort),
                       mDNSVal16(req->u.pm.ReqExt),
+                      &req->u.pm.NATinfo.ExternalAddress,
                       mDNSVal16(req->u.pm.NATinfo.ExternalPort),
                       req->u.pm.NATinfo.NATLease,
                       req->u.pm.NATinfo.Lifetime,
                       mDNSVal16(req->u.pm.NATinfo.ExternalPort),
                       req->u.pm.NATinfo.NATLease,
                       req->u.pm.NATinfo.Lifetime,
-                     get_peer_pid(req->sd, pid_name), pid_name);
+                             req->process_id, req->pid_name);
     else if (req->terminate == addrinfo_termination_callback)
         LogMsgNoIdent("%s DNSServiceGetAddrInfo      %s%s %##s PID[%d](%s)", prefix,
                       req->u.addrinfo.protocol & kDNSServiceProtocol_IPv4 ? "v4" : "  ",
                       req->u.addrinfo.protocol & kDNSServiceProtocol_IPv6 ? "v6" : "  ",
     else if (req->terminate == addrinfo_termination_callback)
         LogMsgNoIdent("%s DNSServiceGetAddrInfo      %s%s %##s PID[%d](%s)", prefix,
                       req->u.addrinfo.protocol & kDNSServiceProtocol_IPv4 ? "v4" : "  ",
                       req->u.addrinfo.protocol & kDNSServiceProtocol_IPv6 ? "v6" : "  ",
-                      req->u.addrinfo.q4.qname.c, get_peer_pid(req->sd, pid_name), pid_name);
+                      req->u.addrinfo.q4.qname.c, req->process_id, req->pid_name);
     else
         LogMsgNoIdent("%s Unrecognized operation %p", prefix, req->terminate);
 }
 
     else
         LogMsgNoIdent("%s Unrecognized operation %p", prefix, req->terminate);
 }
 
+mDNSlocal void GetMcastClients(request_state *req)
+{
+    if (req->terminate == connection_termination)
+    {
+        int num_records = 0, num_ops = 0;
+        const registered_record_entry *p;
+        request_state *r;
+        for (p = req->u.reg_recs; p; p=p->next) 
+            num_records++;
+        for (r = req->next; r; r=r->next) 
+            if (r->primary == req) 
+                num_ops++;
+        for (p = req->u.reg_recs; p; p=p->next)
+        {
+            if (!AuthRecord_uDNS(p->rr))
+                n_mrecords++;
+        }
+        for (r = req->next; r; r=r->next) 
+            if (r->primary == req) 
+                GetMcastClients(r);
+    }
+    else if (req->terminate == regservice_termination_callback)
+    {
+        service_instance *ptr;
+        for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next)
+        {
+            if (!AuthRecord_uDNS(&ptr->srs.RR_SRV))
+                n_mrecords++; 
+        }
+    }
+    else if (req->terminate == browse_termination_callback)
+    {
+        browser_t *blist;
+        for (blist = req->u.browser.browsers; blist; blist = blist->next)
+        {
+            if (mDNSOpaque16IsZero(blist->q.TargetQID))
+                n_mquests++;
+        }
+    }
+    else if (req->terminate == resolve_termination_callback)
+    {
+        if ((mDNSOpaque16IsZero(req->u.resolve.qsrv.TargetQID)) && (req->u.resolve.qsrv.ThisQInterval > 0))
+            n_mquests++;
+    }
+    else if (req->terminate == queryrecord_termination_callback)
+    {
+        if ((mDNSOpaque16IsZero(req->u.queryrecord.q.TargetQID)) && (req->u.queryrecord.q.ThisQInterval > 0))
+            n_mquests++;
+    }
+    else if (req->terminate == addrinfo_termination_callback)
+    {
+        if ((mDNSOpaque16IsZero(req->u.addrinfo.q4.TargetQID)) && (req->u.addrinfo.q4.ThisQInterval > 0))
+            n_mquests++;
+    }
+    else
+    {
+        return;
+    }
+}
+
+
+mDNSlocal void LogMcastClientInfo(request_state *req)
+{
+    if (!req->terminate)
+        LogMcastNoIdent("No operation yet on this socket");
+    else if (req->terminate == connection_termination)
+    {
+        int num_records = 0, num_ops = 0;
+        const registered_record_entry *p;
+        request_state *r;
+        for (p = req->u.reg_recs; p; p=p->next) 
+            num_records++;
+        for (r = req->next; r; r=r->next) 
+            if (r->primary == req) 
+                num_ops++;
+        for (p = req->u.reg_recs; p; p=p->next)
+        {
+            if (!AuthRecord_uDNS(p->rr))
+                LogMcastNoIdent("R: ->  DNSServiceRegisterRecord:  %##s %s PID[%d](%s)", p->rr->resrec.name->c, 
+                                DNSTypeName(p->rr->resrec.rrtype), req->process_id, req->pid_name, i_mcount++);
+        }
+        for (r = req->next; r; r=r->next) 
+            if (r->primary == req) 
+                LogMcastClientInfo(r);
+    }
+    else if (req->terminate == regservice_termination_callback)
+    {
+        service_instance *ptr;
+        for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next)
+        {
+            if (!AuthRecord_uDNS(&ptr->srs.RR_SRV)) 
+                LogMcastNoIdent("R: DNSServiceRegister:  %##s %u/%u PID[%d](%s)", ptr->srs.RR_SRV.resrec.name->c, mDNSVal16(req->u.servicereg.port), 
+                                SRS_PORT(&ptr->srs), req->process_id, req->pid_name, i_mcount++);
+        }
+    }
+    else if (req->terminate == browse_termination_callback)
+    {
+        browser_t *blist;
+        for (blist = req->u.browser.browsers; blist; blist = blist->next)
+        {
+            if (mDNSOpaque16IsZero(blist->q.TargetQID))
+                LogMcastNoIdent("Q: DNSServiceBrowse  %##s %s PID[%d](%s)", blist->q.qname.c, DNSTypeName(blist->q.qtype),
+                                req->process_id, req->pid_name, i_mcount++);
+        }
+    }
+    else if (req->terminate == resolve_termination_callback)
+    {
+        if ((mDNSOpaque16IsZero(req->u.resolve.qsrv.TargetQID)) && (req->u.resolve.qsrv.ThisQInterval > 0))
+            LogMcastNoIdent("Q: DNSServiceResolve  %##s %s PID[%d](%s)", req->u.resolve.qsrv.qname.c, DNSTypeName(req->u.resolve.qsrv.qtype),
+                            req->process_id, req->pid_name, i_mcount++);
+    }
+    else if (req->terminate == queryrecord_termination_callback)
+    {
+        if ((mDNSOpaque16IsZero(req->u.queryrecord.q.TargetQID)) && (req->u.queryrecord.q.ThisQInterval > 0))
+            LogMcastNoIdent("Q: DNSServiceQueryRecord  %##s %s PID[%d](%s)", req->u.queryrecord.q.qname.c, DNSTypeName(req->u.queryrecord.q.qtype), 
+                          req->process_id, req->pid_name, i_mcount++);
+    }
+    else if (req->terminate == addrinfo_termination_callback)
+    {
+        if ((mDNSOpaque16IsZero(req->u.addrinfo.q4.TargetQID)) && (req->u.addrinfo.q4.ThisQInterval > 0))
+            LogMcastNoIdent("Q: DNSServiceGetAddrInfo  %s%s %##s PID[%d](%s)",
+                          req->u.addrinfo.protocol & kDNSServiceProtocol_IPv4 ? "v4" : "  ",
+                          req->u.addrinfo.protocol & kDNSServiceProtocol_IPv6 ? "v6" : "  ",
+                          req->u.addrinfo.q4.qname.c, req->process_id, req->pid_name, i_mcount++);
+    }
+    else
+    {
+        return;
+    }
+    
+}
+
 mDNSlocal char *RecordTypeName(mDNSu8 rtype)
 {
     switch (rtype)
 mDNSlocal char *RecordTypeName(mDNSu8 rtype)
 {
     switch (rtype)
@@ -4444,7 +5357,6 @@ mDNSlocal void LogEtcHosts(mDNS *const m)
                         LogMsgNoIdent(" %s   %u  %s", RecordTypeName(ar->resrec.RecordType), scopeid, ARDisplayString(m, ar));
                     }
                 }
                         LogMsgNoIdent(" %s   %u  %s", RecordTypeName(ar->resrec.RecordType), scopeid, ARDisplayString(m, ar));
                     }
                 }
-                usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
             }
     }
 
             }
     }
 
@@ -4472,13 +5384,44 @@ mDNSlocal void LogLocalOnlyAuthRecords(mDNS *const m)
                     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));
                     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>");
 }
 
             }
     }
 
     if (showheader) LogMsgNoIdent("<None>");
 }
 
+mDNSlocal char *AnonInfoToString(AnonymousInfo *ai, char *anonstr, int anstrlen)
+{
+    anonstr[0] = 0;
+    if (ai && ai->AnonData)
+    {
+        return (AnonDataToString(ai->AnonData, ai->AnonDataLen, anonstr, anstrlen));
+    }
+    return anonstr;
+}
+
+mDNSlocal void LogOneAuthRecord(mDNS *const m, const AuthRecord *ar, mDNSs32 now, const char *const ifname)
+{
+    char anstr[256];
+    if (AuthRecord_uDNS(ar))
+    {
+        LogMsgNoIdent("%7d %7d %7d %7d %s",
+                      ar->ThisAPInterval / mDNSPlatformOneSecond,
+                      (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond,
+                      ar->expire ? (ar->expire - now) / mDNSPlatformOneSecond : 0,
+                      ar->state, ARDisplayString(m, ar));
+    }
+    else
+    {
+        LogMsgNoIdent("%7d %7d %7d %7s %s%s",
+                      ar->ThisAPInterval / mDNSPlatformOneSecond,
+                      ar->AnnounceCount ? (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond : 0,
+                      ar->TimeExpire    ? (ar->TimeExpire                      - now) / mDNSPlatformOneSecond : 0,
+                      ifname ? ifname : "ALL",
+                      ARDisplayString(m, ar), AnonInfoToString(ar->resrec.AnonInfo, anstr, sizeof(anstr)));
+    }
+}
+
 mDNSlocal void LogAuthRecords(mDNS *const m, const mDNSs32 now, AuthRecord *ResourceRecords, int *proxy)
 {
     mDNSBool showheader = mDNStrue;
 mDNSlocal void LogAuthRecords(mDNS *const m, const mDNSs32 now, AuthRecord *ResourceRecords, int *proxy)
 {
     mDNSBool showheader = mDNStrue;
@@ -4502,28 +5445,152 @@ mDNSlocal void LogAuthRecords(mDNS *const m, const mDNSs32 now, AuthRecord *Reso
                     LogMsgNoIdent("Proxying for %.6a seq %d",                                &owner.HMAC,                               owner.seq);
             }
             if (AuthRecord_uDNS(ar))
                     LogMsgNoIdent("Proxying for %.6a seq %d",                                &owner.HMAC,                               owner.seq);
             }
             if (AuthRecord_uDNS(ar))
-                LogMsgNoIdent("%7d %7d %7d %7d %s",
-                              ar->ThisAPInterval / mDNSPlatformOneSecond,
-                              (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond,
-                              ar->expire ? (ar->expire - now) / mDNSPlatformOneSecond : 0,
-                              ar->state, ARDisplayString(m, ar));
+            {
+                LogOneAuthRecord(m, ar, now, ifname);
+            }
             else if (ar->ARType == AuthRecordLocalOnly)
             else if (ar->ARType == AuthRecordLocalOnly)
+            {
                 LogMsgNoIdent("                             LO %s", ARDisplayString(m, ar));
                 LogMsgNoIdent("                             LO %s", ARDisplayString(m, ar));
+            }
             else if (ar->ARType == AuthRecordP2P)
             else if (ar->ARType == AuthRecordP2P)
+            {
                 LogMsgNoIdent("                             PP %s", ARDisplayString(m, ar));
                 LogMsgNoIdent("                             PP %s", ARDisplayString(m, ar));
+            }
             else
             else
-                LogMsgNoIdent("%7d %7d %7d %7s %s",
-                              ar->ThisAPInterval / mDNSPlatformOneSecond,
-                              ar->AnnounceCount ? (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond : 0,
-                              ar->TimeExpire    ? (ar->TimeExpire                      - now) / mDNSPlatformOneSecond : 0,
-                              ifname ? ifname : "ALL",
-                              ARDisplayString(m, ar));
-            usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
+            {
+                LogOneAuthRecord(m, ar, now, ifname);
+                if (ar->resrec.AnonInfo)
+                {
+                    ResourceRecord *nsec3 = ar->resrec.AnonInfo->nsec3RR;
+                    // We just print the values from the AuthRecord to keep it nicely aligned though
+                    // all we want here is the nsec3 information.
+                    LogMsgNoIdent("%7d %7d %7d %7s %s",
+                                  ar->ThisAPInterval / mDNSPlatformOneSecond,
+                                  ar->AnnounceCount ? (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond : 0,
+                                  ar->TimeExpire    ? (ar->TimeExpire                      - now) / mDNSPlatformOneSecond : 0,
+                                  ifname ? ifname : "ALL",
+                                  RRDisplayString(m, nsec3));
+                }
+            }
         }
     }
     if (showheader) LogMsgNoIdent("<None>");
 }
 
         }
     }
     if (showheader) LogMsgNoIdent("<None>");
 }
 
+mDNSlocal void PrintOneCacheRecord(mDNS *const m, const CacheRecord *cr, mDNSu32 slot, const mDNSu32 remain, const char *ifname, mDNSu32 *CacheUsed)
+{
+    LogMsgNoIdent("%3d %s%8d %-7s%s %-6s%s",
+                  slot,
+                  cr->CRActiveQuestion ? "*" : " ",
+                  remain,
+                  ifname ? ifname : "-U-",
+                  (cr->resrec.RecordType == kDNSRecordTypePacketNegative)  ? "-" :
+                  (cr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? " " : "+",
+                  DNSTypeName(cr->resrec.rrtype),
+                  CRDisplayString(m, cr));
+    (*CacheUsed)++;
+}
+
+mDNSlocal void PrintCachedRecords(mDNS *const m, const CacheRecord *cr, mDNSu32 slot, const mDNSu32 remain, const char *ifname, mDNSu32 *CacheUsed)
+{
+    CacheRecord *nsec;
+    CacheRecord *soa;
+    nsec = cr->nsec;
+
+    // The records that are cached under the main cache record like nsec, soa don't have
+    // their own lifetime. If the main cache record expires, they also expire.
+    while (nsec)
+    {
+        PrintOneCacheRecord(m, nsec, slot, remain, ifname, CacheUsed);
+        nsec = nsec->next;
+    }
+    soa = cr->soa;
+    if (soa)
+    {
+        PrintOneCacheRecord(m, soa, slot, remain, ifname, CacheUsed);
+    }
+    if (cr->resrec.AnonInfo)
+    {
+        ResourceRecord *nsec3 = cr->resrec.AnonInfo->nsec3RR;
+        // Even though it is a resource record, we print the sameway
+        // as a cache record so that it aligns properly.
+        if (nsec3)
+        {
+            LogMsgNoIdent("%3d %s%8d %-7s%s %-6s%s",
+                          slot,
+                          " ",
+                          remain,
+                          ifname ? ifname : "-U-",
+                          (nsec3->RecordType == kDNSRecordTypePacketNegative)  ? "-" :
+                          (nsec3->RecordType & kDNSRecordTypePacketUniqueMask) ? " " : "+",
+                          DNSTypeName(nsec3->rrtype),
+                          RRDisplayString(m, nsec3));
+        }
+    }
+}
+
+mDNSlocal char *AnonDataToString(const mDNSu8 *ad, int adlen, char *adstr, int adstrlen)
+{
+    adstr[0] = 0;
+    if (ad)
+    {
+        int len;
+        char *orig = adstr;
+
+        // If the caller is lazy to compute the length, we do it for them.
+        if (!adlen)
+            len = strlen((const char *)ad);
+        else
+            len = adlen;
+
+        // Print the anondata within brackets. Hence, we need space for two
+        // brackets and a NULL byte.
+        if (len > (adstrlen - 3))
+            len = adstrlen - 3;
+
+        *adstr++ = '(';
+        mDNSPlatformMemCopy(adstr, ad, len);
+        adstr[len] = ')';
+        adstr[len+1] = 0;
+        return orig;
+    }
+    return adstr;
+}
+
+mDNSexport void LogMDNSStatistics(mDNS *const m)
+{
+    LogMsgNoIdent("--- MDNS Statistics ---");
+    
+    LogMsgNoIdent("Name Conflicts                 %u", m->mDNSStats.NameConflicts);
+    LogMsgNoIdent("KnownUnique Name Conflicts     %u", m->mDNSStats.KnownUniqueNameConflicts);
+    LogMsgNoIdent("Duplicate Query Suppressions   %u", m->mDNSStats.DupQuerySuppressions);
+    LogMsgNoIdent("KA Suppressions                %u", m->mDNSStats.KnownAnswerSuppressions);
+    LogMsgNoIdent("KA Multiple Packets            %u", m->mDNSStats.KnownAnswerMultiplePkts);
+    LogMsgNoIdent("Poof Cache Deletions           %u", m->mDNSStats.PoofCacheDeletions);
+    LogMsgNoIdent("--------------------------------");
+
+    LogMsgNoIdent("Multicast packets Sent         %u", m->MulticastPacketsSent);
+    LogMsgNoIdent("Multicast packets Received     %u", m->MPktNum);
+    LogMsgNoIdent("Remote Subnet packets          %u", m->RemoteSubnet);
+    LogMsgNoIdent("QU questions  received         %u", m->mDNSStats.UnicastBitInQueries);
+    LogMsgNoIdent("Normal multicast questions     %u", m->mDNSStats.NormalQueries);
+    LogMsgNoIdent("Answers for questions          %u", m->mDNSStats.MatchingAnswersForQueries);
+    LogMsgNoIdent("Unicast responses              %u", m->mDNSStats.UnicastResponses);
+    LogMsgNoIdent("Multicast responses            %u", m->mDNSStats.MulticastResponses);
+    LogMsgNoIdent("Unicast response Demotions     %u", m->mDNSStats.UnicastDemotedToMulticast);
+    LogMsgNoIdent("--------------------------------");
+
+    LogMsgNoIdent("Sleeps                         %u", m->mDNSStats.Sleeps);
+    LogMsgNoIdent("Wakeups                        %u", m->mDNSStats.Wakes);
+    LogMsgNoIdent("Interface UP events            %u", m->mDNSStats.InterfaceUp);
+    LogMsgNoIdent("Interface UP Flap events       %u", m->mDNSStats.InterfaceUpFlap);
+    LogMsgNoIdent("Interface Down events          %u", m->mDNSStats.InterfaceDown);
+    LogMsgNoIdent("Interface DownFlap events      %u", m->mDNSStats.InterfaceDownFlap);
+    LogMsgNoIdent("Cache refresh queries          %u", m->mDNSStats.CacheRefreshQueries);
+    LogMsgNoIdent("Cache refreshed                %u", m->mDNSStats.CacheRefreshed);
+    LogMsgNoIdent("Wakeup on Resolves             %u", m->mDNSStats.WakeOnResolves);
+}
+
 mDNSexport void udsserver_info(mDNS *const m)
 {
     const mDNSs32 now = mDNS_TimeNow(m);
 mDNSexport void udsserver_info(mDNS *const m)
 {
     const mDNSs32 now = mDNS_TimeNow(m);
@@ -4540,6 +5607,7 @@ mDNSexport void udsserver_info(mDNS *const m)
     LogMsgNoIdent("------------ Cache -------------");
     LogMsgNoIdent("Slt Q     TTL if     U Type rdlen");
     for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
     LogMsgNoIdent("------------ Cache -------------");
     LogMsgNoIdent("Slt Q     TTL if     U Type rdlen");
     for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
+    {
         for (cg = m->rrcache_hash[slot]; cg; cg=cg->next)
         {
             CacheUsed++;    // Count one cache entity for the CacheGroup object
         for (cg = m->rrcache_hash[slot]; cg; cg=cg->next)
         {
             CacheUsed++;    // Count one cache entity for the CacheGroup object
@@ -4547,40 +5615,16 @@ mDNSexport void udsserver_info(mDNS *const m)
             {
                 const mDNSs32 remain = cr->resrec.rroriginalttl - (now - cr->TimeRcvd) / mDNSPlatformOneSecond;
                 const char *ifname;
             {
                 const mDNSs32 remain = cr->resrec.rroriginalttl - (now - cr->TimeRcvd) / mDNSPlatformOneSecond;
                 const char *ifname;
-                CacheRecord *nsec;
                 mDNSInterfaceID InterfaceID = cr->resrec.InterfaceID;
                 if (!InterfaceID && cr->resrec.rDNSServer && cr->resrec.rDNSServer->scoped)
                     InterfaceID = cr->resrec.rDNSServer->interface;
                 ifname = InterfaceNameForID(m, InterfaceID);
                 mDNSInterfaceID InterfaceID = cr->resrec.InterfaceID;
                 if (!InterfaceID && cr->resrec.rDNSServer && cr->resrec.rDNSServer->scoped)
                     InterfaceID = cr->resrec.rDNSServer->interface;
                 ifname = InterfaceNameForID(m, InterfaceID);
-                CacheUsed++;
                 if (cr->CRActiveQuestion) CacheActive++;
                 if (cr->CRActiveQuestion) CacheActive++;
-                LogMsgNoIdent("%3d %s%8d %-7s%s %-6s%s",
-                              slot,
-                              cr->CRActiveQuestion ? "*" : " ",
-                              remain,
-                              ifname ? ifname : "-U-",
-                              (cr->resrec.RecordType == kDNSRecordTypePacketNegative)  ? "-" :
-                              (cr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? " " : "+",
-                              DNSTypeName(cr->resrec.rrtype),
-                              CRDisplayString(m, cr));
-                nsec = cr->nsec;
-                while (nsec)
-                {
-                    LogMsgNoIdent("%3d %s%8d %-7s%s %-6s%s",
-                                  slot,
-                                  nsec->CRActiveQuestion ? "*" : " ",
-                                  remain,
-                                  ifname ? ifname : "-U-",
-                                  (nsec->resrec.RecordType == kDNSRecordTypePacketNegative)  ? "-" :
-                                  (nsec->resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? " " : "+",
-                                  DNSTypeName(nsec->resrec.rrtype),
-                                  CRDisplayString(m, nsec));
-                    CacheUsed++;
-                    nsec = nsec->next;
-                }
-                usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
+                PrintOneCacheRecord(m, cr, slot, remain, ifname, &CacheUsed);
+                PrintCachedRecords(m, cr, slot, remain, ifname, &CacheUsed);
             }
         }
             }
         }
+    }
 
     if (m->rrcache_totalused != CacheUsed)
         LogMsgNoIdent("Cache use mismatch: rrcache_totalused is %lu, true count %lu", m->rrcache_totalused, CacheUsed);
 
     if (m->rrcache_totalused != CacheUsed)
         LogMsgNoIdent("Cache use mismatch: rrcache_totalused is %lu, true count %lu", m->rrcache_totalused, CacheUsed);
@@ -4610,6 +5654,7 @@ mDNSexport void udsserver_info(mDNS *const m)
     if (!m->Questions) LogMsgNoIdent("<None>");
     else
     {
     if (!m->Questions) LogMsgNoIdent("<None>");
     else
     {
+        char anonstr[256];
         CacheUsed = 0;
         CacheActive = 0;
         LogMsgNoIdent("   Int  Next if     T  NumAns VDNS    Qptr     DupOf    SU SQ Type Name");
         CacheUsed = 0;
         CacheActive = 0;
         LogMsgNoIdent("   Int  Next if     T  NumAns VDNS    Qptr     DupOf    SU SQ Type Name");
@@ -4620,14 +5665,15 @@ mDNSexport void udsserver_info(mDNS *const m)
             char *ifname = InterfaceNameForID(m, q->InterfaceID);
             CacheUsed++;
             if (q->ThisQInterval) CacheActive++;
             char *ifname = InterfaceNameForID(m, q->InterfaceID);
             CacheUsed++;
             if (q->ThisQInterval) CacheActive++;
-            LogMsgNoIdent("%6d%6d %-7s%s%s %5d 0x%x%x 0x%p 0x%p %1d %2d %-5s%##s%s",
+            LogMsgNoIdent("%6d%6d %-7s%s%s %5d 0x%x%x 0x%p 0x%p %1d %2d %-5s%##s%s%s",
                           i, n,
                           ifname ? ifname : mDNSOpaque16IsZero(q->TargetQID) ? "" : "-U-",
                           mDNSOpaque16IsZero(q->TargetQID) ? (q->LongLived ? "l" : " ") : (q->LongLived ? "L" : "O"),
                           PrivateQuery(q)    ? "P" : q->ValidationRequired ? "V" : q->ValidatingResponse ? "R" : " ",
                           q->CurrentAnswers, q->validDNSServers.l[1], q->validDNSServers.l[0], q, q->DuplicateOf,
                           i, n,
                           ifname ? ifname : mDNSOpaque16IsZero(q->TargetQID) ? "" : "-U-",
                           mDNSOpaque16IsZero(q->TargetQID) ? (q->LongLived ? "l" : " ") : (q->LongLived ? "L" : "O"),
                           PrivateQuery(q)    ? "P" : q->ValidationRequired ? "V" : q->ValidatingResponse ? "R" : " ",
                           q->CurrentAnswers, q->validDNSServers.l[1], q->validDNSServers.l[0], q, q->DuplicateOf,
-                          q->SuppressUnusable, q->SuppressQuery, DNSTypeName(q->qtype), q->qname.c, q->DuplicateOf ? " (dup)" : "");
-            usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
+                          q->SuppressUnusable, q->SuppressQuery, DNSTypeName(q->qtype), q->qname.c,
+                          AnonInfoToString(q->AnonInfo, anonstr, sizeof(anonstr)),
+                          q->DuplicateOf ? " (dup)" : "");
         }
         LogMsgNoIdent("%lu question%s; %lu active", CacheUsed, CacheUsed > 1 ? "s" : "", CacheActive);
     }
         }
         LogMsgNoIdent("%lu question%s; %lu active", CacheUsed, CacheUsed > 1 ? "s" : "", CacheActive);
     }
@@ -4638,11 +5684,11 @@ mDNSexport void udsserver_info(mDNS *const m)
             LogMsgNoIdent("                       %5d  %-6s%##s%s",
                           q->CurrentAnswers, DNSTypeName(q->qtype), q->qname.c, q->DuplicateOf ? " (dup)" : "");
 
             LogMsgNoIdent("                       %5d  %-6s%##s%s",
                           q->CurrentAnswers, DNSTypeName(q->qtype), q->qname.c, q->DuplicateOf ? " (dup)" : "");
 
-    LogMsgNoIdent("---- Active Client Requests ----");
+    LogMsgNoIdent("---- Active UDS Client Requests ----");
     if (!all_requests) LogMsgNoIdent("<None>");
     else
     {
     if (!all_requests) LogMsgNoIdent("<None>");
     else
     {
-        const request_state *req, *r;
+        request_state *req, *r;
         for (req = all_requests; req; req=req->next)
         {
             if (req->primary)   // If this is a subbordinate operation, check that the parent is in the list
         for (req = all_requests; req; req=req->next)
         {
             if (req->primary)   // If this is a subbordinate operation, check that the parent is in the list
@@ -4657,24 +5703,30 @@ foundparent:;
     }
 
     LogMsgNoIdent("-------- NAT Traversals --------");
     }
 
     LogMsgNoIdent("-------- NAT Traversals --------");
-    if (!m->NATTraversals) LogMsgNoIdent("<None>");
-    else
+    LogMsgNoIdent("ExtAddress %.4a Retry %d Interval %d",
+                  &m->ExtAddress,
+                  m->retryGetAddr ? (m->retryGetAddr - now) / mDNSPlatformOneSecond : 0,
+                  m->retryIntervalGetAddr / mDNSPlatformOneSecond);
+    if (m->NATTraversals)
     {
         const NATTraversalInfo *nat;
         for (nat = m->NATTraversals; nat; nat=nat->next)
         {
     {
         const NATTraversalInfo *nat;
         for (nat = m->NATTraversals; nat; nat=nat->next)
         {
-            if (nat->Protocol)
-                LogMsgNoIdent("%p %s Int %5d Ext %5d Err %d Retry %5d Interval %5d Expire %5d",
-                              nat, nat->Protocol == NATOp_MapTCP ? "TCP" : "UDP",
-                              mDNSVal16(nat->IntPort), mDNSVal16(nat->ExternalPort), nat->Result,
-                              nat->retryPortMap ? (nat->retryPortMap - now) / mDNSPlatformOneSecond : 0,
-                              nat->retryInterval / mDNSPlatformOneSecond,
-                              nat->ExpiryTime ? (nat->ExpiryTime - now) / mDNSPlatformOneSecond : 0);
-            else
-                LogMsgNoIdent("%p Address Request               Retry %5d Interval %5d", nat,
-                              (m->retryGetAddr - now) / mDNSPlatformOneSecond,
-                              m->retryIntervalGetAddr / mDNSPlatformOneSecond);
-            usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
+            LogMsgNoIdent("%p %s Int %5d %s Err %d Retry %5d Interval %5d Expire %5d Req %.4a:%d Ext %.4a:%d",
+                          nat,
+                          nat->Protocol ? (nat->Protocol == NATOp_MapTCP ? "TCP" : "UDP") : "ADD",
+                          mDNSVal16(nat->IntPort),
+                          (nat->lastSuccessfulProtocol == NATTProtocolNone    ? "None    " :
+                           nat->lastSuccessfulProtocol == NATTProtocolNATPMP  ? "NAT-PMP " :
+                           nat->lastSuccessfulProtocol == NATTProtocolUPNPIGD ? "UPnP/IGD" :
+                           nat->lastSuccessfulProtocol == NATTProtocolPCP     ? "PCP     " :
+                           /* else */                                           "Unknown " ),
+                          nat->Result,
+                          nat->retryPortMap ? (nat->retryPortMap - now) / mDNSPlatformOneSecond : 0,
+                          nat->retryInterval / mDNSPlatformOneSecond,
+                          nat->ExpiryTime ? (nat->ExpiryTime - now) / mDNSPlatformOneSecond : 0,
+                          &nat->NewAddress, mDNSVal16(nat->RequestedPort),
+                          &nat->ExternalAddress, mDNSVal16(nat->ExternalPort));
         }
     }
 
         }
     }
 
@@ -4716,11 +5768,10 @@ foundparent:;
                   m->SleepState == SleepState_Sleeping     ? "Sleeping"     : "?",
                   m->SleepSeqNum);
 
                   m->SleepState == SleepState_Sleeping     ? "Sleeping"     : "?",
                   m->SleepSeqNum);
 
-    LogMsgNoIdent("m->clearIgnoreNA %d", m->clearIgnoreNA);
-
     if (!m->SPSSocket) LogMsgNoIdent("Not offering Sleep Proxy Service");
     if (!m->SPSSocket) LogMsgNoIdent("Not offering Sleep Proxy Service");
+#ifndef SPC_DISABLED
     else LogMsgNoIdent("Offering Sleep Proxy Service: %#s", m->SPSRecords.RR_SRV.resrec.name->c);
     else LogMsgNoIdent("Offering Sleep Proxy Service: %#s", m->SPSRecords.RR_SRV.resrec.name->c);
-
+#endif
     if (m->ProxyRecords == ProxyA + ProxyD) LogMsgNoIdent("ProxyRecords: %d + %d = %d", ProxyA, ProxyD, ProxyA + ProxyD);
     else LogMsgNoIdent("ProxyRecords: MISMATCH %d + %d = %d ≠ %d", ProxyA, ProxyD, ProxyA + ProxyD, m->ProxyRecords);
 
     if (m->ProxyRecords == ProxyA + ProxyD) LogMsgNoIdent("ProxyRecords: %d + %d = %d", ProxyA, ProxyD, ProxyA + ProxyD);
     else LogMsgNoIdent("ProxyRecords: MISMATCH %d + %d = %d ≠ %d", ProxyA, ProxyD, ProxyA + ProxyD, m->ProxyRecords);
 
@@ -4742,8 +5793,11 @@ foundparent:;
             LogMsgNoIdent("%##s %s", s->domain.c, ifname ? ifname : "");
         }
     }
             LogMsgNoIdent("%##s %s", s->domain.c, ifname ? ifname : "");
         }
     }
-    LogMsgNoIdent("--- Trust Anchors --");
-    if (!m->TrustAnchors) LogMsgNoIdent("<None>");
+    LogInfo("--- Trust Anchors ---");
+    if (!m->TrustAnchors) 
+    {
+        LogInfo("<None>");
+    }
     else
     {
         TrustAnchor *ta;
     else
     {
         TrustAnchor *ta;
@@ -4754,11 +5808,42 @@ foundparent:;
         {
             mDNSPlatformFormatTime((unsigned long)ta->validFrom, fromTimeBuf, sizeof(fromTimeBuf));
             mDNSPlatformFormatTime((unsigned long)ta->validUntil, untilTimeBuf, sizeof(untilTimeBuf));
         {
             mDNSPlatformFormatTime((unsigned long)ta->validFrom, fromTimeBuf, sizeof(fromTimeBuf));
             mDNSPlatformFormatTime((unsigned long)ta->validUntil, untilTimeBuf, sizeof(untilTimeBuf));
-            LogMsgNoIdent("%##s %d %d %d %d %s %s", ta->zone.c, ta->rds.keyTag,
+            LogInfo("%##s %d %d %d %d %s %s", ta->zone.c, ta->rds.keyTag,
                 ta->rds.alg, ta->rds.digestType, ta->digestLen, fromTimeBuf, untilTimeBuf);
         }
     }
 
                 ta->rds.alg, ta->rds.digestType, ta->digestLen, fromTimeBuf, untilTimeBuf);
         }
     }
 
+    LogInfo("--- DNSSEC Statistics ---");
+
+    LogInfo("Next Stats Time                 %u", m->NextStatLogTime - mDNSPlatformUTC());
+    LogMsgNoIdent("Unicast Cache size              %u", m->rrcache_totalused_unicast);
+    LogInfo("DNSSEC  Cache size              %u", m->DNSSECStats.TotalMemUsed);
+    if (m->rrcache_totalused_unicast)
+        LogInfo("DNSSEC  usage percentage        %u", ((unsigned long)(m->DNSSECStats.TotalMemUsed * 100))/m->rrcache_totalused_unicast);
+    LogInfo("DNSSEC  Extra Packets (0 to 2)  %u", m->DNSSECStats.ExtraPackets0);
+    LogInfo("DNSSEC  Extra Packets (3 to 6)  %u", m->DNSSECStats.ExtraPackets3);
+    LogInfo("DNSSEC  Extra Packets (7 to 9)  %u", m->DNSSECStats.ExtraPackets7);
+    LogInfo("DNSSEC  Extra Packets ( >= 10)  %u", m->DNSSECStats.ExtraPackets10);
+
+    LogInfo("DNSSEC  Latency (0 to 4ms)      %u", m->DNSSECStats.Latency0);
+    LogInfo("DNSSEC  Latency (4 to 9ms)      %u", m->DNSSECStats.Latency5);
+    LogInfo("DNSSEC  Latency (10 to 19ms)    %u", m->DNSSECStats.Latency10);
+    LogInfo("DNSSEC  Latency (20 to 49ms)    %u", m->DNSSECStats.Latency20);
+    LogInfo("DNSSEC  Latency (50 to 99ms)    %u", m->DNSSECStats.Latency50);
+    LogInfo("DNSSEC  Latency (   >=100ms)    %u", m->DNSSECStats.Latency100);
+
+    LogInfo("DNSSEC  Secure Status           %u", m->DNSSECStats.SecureStatus);
+    LogInfo("DNSSEC  Insecure Status         %u", m->DNSSECStats.InsecureStatus);
+    LogInfo("DNSSEC  Indeterminate Status    %u", m->DNSSECStats.IndeterminateStatus);
+    LogInfo("DNSSEC  Bogus Status            %u", m->DNSSECStats.BogusStatus);
+    LogInfo("DNSSEC  NoResponse Status       %u", m->DNSSECStats.NoResponseStatus);
+    LogInfo("DNSSEC  Probes sent             %u", m->DNSSECStats.NumProbesSent);
+    LogInfo("DNSSEC  Msg Size (<=1024)       %u", m->DNSSECStats.MsgSize0);
+    LogInfo("DNSSEC  Msg Size (<=2048)       %u", m->DNSSECStats.MsgSize1);
+    LogInfo("DNSSEC  Msg Size (> 2048)       %u", m->DNSSECStats.MsgSize2);
+
+    LogMDNSStatistics(m);
+
     LogMsgNoIdent("---- Task Scheduling Timers ----");
 
     if (!m->NewQuestions)
     LogMsgNoIdent("---- Task Scheduling Timers ----");
 
     if (!m->NewQuestions)
@@ -4782,6 +5867,11 @@ foundparent:;
     LogMsgNoIdent("SPSProxyListChanged%s", m->SPSProxyListChanged ? "" : " <NONE>");
     LogMsgNoIdent("LocalRemoveEvents%s",   m->LocalRemoveEvents   ? "" : " <NONE>");
     LogMsgNoIdent("m->AutoTunnelRelayAddr %.16a", &m->AutoTunnelRelayAddr);
     LogMsgNoIdent("SPSProxyListChanged%s", m->SPSProxyListChanged ? "" : " <NONE>");
     LogMsgNoIdent("LocalRemoveEvents%s",   m->LocalRemoveEvents   ? "" : " <NONE>");
     LogMsgNoIdent("m->AutoTunnelRelayAddr %.16a", &m->AutoTunnelRelayAddr);
+    LogMsgNoIdent("m->WABBrowseQueriesCount %d", m->WABBrowseQueriesCount);
+    LogMsgNoIdent("m->WABLBrowseQueriesCount %d", m->WABLBrowseQueriesCount);
+    LogMsgNoIdent("m->WABRegQueriesCount %d", m->WABRegQueriesCount);
+    LogMsgNoIdent("m->mDNSOppCaching %d", m->mDNSOppCaching);
+    LogMsgNoIdent("m->AutoTargetServices %d", m->AutoTargetServices);
 
 #define LogTimer(MSG,T) LogMsgNoIdent( MSG " %08X %11d  %08X %11d", (T), (T), (T)-now, (T)-now)
 
 
 #define LogTimer(MSG,T) LogMsgNoIdent( MSG " %08X %11d  %08X %11d", (T), (T), (T)-now, (T)-now)
 
@@ -4930,7 +6020,7 @@ mDNSexport mDNSs32 udsserver_idle(mDNSs32 nextevent)
             {
                 r->u.resolve.ReportTime = 0;
                 LogMsgNoIdent("Client application bug PID[%d](%s) : DNSServiceResolve(%##s) active for over two minutes. "
             {
                 r->u.resolve.ReportTime = 0;
                 LogMsgNoIdent("Client application bug PID[%d](%s) : DNSServiceResolve(%##s) active for over two minutes. "
-                              "This places considerable burden on the network.", get_peer_pid(r->sd, pid_name), pid_name, r->u.resolve.qsrv.qname.c);
+                              "This places considerable burden on the network.", r->process_id, r->pid_name, r->u.resolve.qsrv.qname.c);
             }
 
         // Note: Only primary req's have reply lists, not subordinate req's.
             }
 
         // Note: Only primary req's have reply lists, not subordinate req's.
@@ -4951,7 +6041,7 @@ mDNSexport mDNSs32 udsserver_idle(mDNSs32 nextevent)
             }
             else if (result == t_terminated || result == t_error)
             {
             }
             else if (result == t_terminated || result == t_error)
             {
-                LogMsg("%3d: Could not write data to clientPID[%d](%s)  because of error - aborting connection", r->sd, get_peer_pid(r->sd, pid_name), pid_name);
+                LogMsg("%3d: Could not write data to clientPID[%d](%s)  because of error - aborting connection", r->sd, r->process_id, r->pid_name);
                 LogClientInfo(&mDNSStorage, r);
                 abort_request(r);
             }
                 LogClientInfo(&mDNSStorage, r);
                 abort_request(r);
             }
@@ -4977,10 +6067,10 @@ mDNSexport mDNSs32 udsserver_idle(mDNSs32 nextevent)
                     x=x->next; 
                 }
                 LogMsg("%3d: Could not write data to client PID[%d](%s) after %ld seconds, %d repl%s waiting",
                     x=x->next; 
                 }
                 LogMsg("%3d: Could not write data to client PID[%d](%s) after %ld seconds, %d repl%s waiting",
-                       r->sd, get_peer_pid(r->sd, pid_name), pid_name, (now - r->time_blocked) / mDNSPlatformOneSecond, num, num == 1 ? "y" : "ies");
+                       r->sd, r->process_id, r->pid_name, (now - r->time_blocked) / mDNSPlatformOneSecond, num, num == 1 ? "y" : "ies");
                 if (++r->unresponsiveness_reports >= 60)
                 {
                 if (++r->unresponsiveness_reports >= 60)
                 {
-                    LogMsg("%3d: Client PID[%d](%s) unresponsive; aborting connection", r->sd, get_peer_pid(r->sd, pid_name), pid_name);
+                    LogMsg("%3d: Client PID[%d](%s) unresponsive; aborting connection", r->sd, r->process_id, r->pid_name);
                     LogClientInfo(&mDNSStorage, r);
                     abort_request(r);
                 }
                     LogClientInfo(&mDNSStorage, r);
                     abort_request(r);
                 }
@@ -5007,7 +6097,7 @@ struct CompileTimeAssertionChecks_uds_daemon
     char sizecheck_request_state          [(sizeof(request_state)           <= 2000) ? 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_request_state          [(sizeof(request_state)           <= 2000) ? 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)               <= 1050) ? 1 : -1];
+    char sizecheck_browser_t              [(sizeof(browser_t)               <= 1096) ? 1 : -1];
     char sizecheck_reply_hdr              [(sizeof(reply_hdr)               <=   12) ? 1 : -1];
     char sizecheck_reply_state            [(sizeof(reply_state)             <=   64) ? 1 : -1];
 };
     char sizecheck_reply_hdr              [(sizeof(reply_hdr)               <=   12) ? 1 : -1];
     char sizecheck_reply_state            [(sizeof(reply_state)             <=   64) ? 1 : -1];
 };
index 525494697efe7583d61425ae996f645e83afa393..ca361172d16f1c9b9a67ca5dd2f2d23ba6c4c6ae 100644 (file)
@@ -34,6 +34,11 @@ extern mDNSs32 udsserver_idle(mDNSs32 nextevent);
 extern void udsserver_info(mDNS *const m);  // print out info about current state
 extern void udsserver_handle_configchange(mDNS *const m);
 extern int udsserver_exit(void);    // should be called prior to app exit
 extern void udsserver_info(mDNS *const m);  // print out info about current state
 extern void udsserver_handle_configchange(mDNS *const m);
 extern int udsserver_exit(void);    // should be called prior to app exit
+extern void LogMcastStateInfo(mDNS *const m, mDNSBool mflag, mDNSBool start, mDNSBool mstatelog);
+#define LogMcastQ       (mDNS_McastLoggingEnabled == 0) ? ((void)0) : LogMcastQuestion
+#define LogMcastS       (mDNS_McastLoggingEnabled == 0) ? ((void)0) : LogMcastService
+#define LogMcast        (mDNS_McastLoggingEnabled == 0) ? ((void)0) : LogMsg
+#define LogMcastNoIdent (mDNS_McastLoggingEnabled == 0) ? ((void)0) : LogMsgNoIdent
 
 /* Routines that uds_daemon expects to link against: */
 
 
 /* Routines that uds_daemon expects to link against: */
 
@@ -50,16 +55,17 @@ extern mDNS mDNSStorage;
 extern DNameListElem *AutoRegistrationDomains;
 extern DNameListElem *AutoBrowseDomains;
 
 extern DNameListElem *AutoRegistrationDomains;
 extern DNameListElem *AutoBrowseDomains;
 
-extern mDNSs32 ChopSubTypes(char *regtype);
-extern AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p);
+extern mDNSs32 ChopSubTypes(char *regtype, char **AnonData);
+extern AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p, char **AnonData);
 extern int CountExistingRegistrations(domainname *srv, mDNSIPPort port);
 extern void FreeExtraRR(mDNS *const m, AuthRecord *const rr, mStatus result);
 extern int CountPeerRegistrations(mDNS *const m, ServiceRecordSet *const srs);
 
 #if APPLE_OSX_mDNSResponder
 extern int CountExistingRegistrations(domainname *srv, mDNSIPPort port);
 extern void FreeExtraRR(mDNS *const m, AuthRecord *const rr, mStatus result);
 extern int CountPeerRegistrations(mDNS *const m, ServiceRecordSet *const srs);
 
 #if APPLE_OSX_mDNSResponder
+
 extern void machserver_automatic_browse_domain_changed(const domainname *d, mDNSBool add);
 extern void machserver_automatic_registration_domain_changed(const domainname *d, mDNSBool add);
 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
+// D2D interface support
 extern void external_start_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const type, DNS_TypeValues qtype, DNSServiceFlags flags);
 extern void external_stop_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const type, DNS_TypeValues qtype, DNSServiceFlags flags);
 extern void external_start_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags);
 extern void external_start_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const type, DNS_TypeValues qtype, DNSServiceFlags flags);
 extern void external_stop_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const type, DNS_TypeValues qtype, DNSServiceFlags flags);
 extern void external_start_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags);
@@ -67,7 +73,9 @@ extern void external_stop_advertising_service(const ResourceRecord *const resour
 extern void external_start_resolving_service(mDNSInterfaceID InterfaceID, const domainname *const fqdn, DNSServiceFlags flags);
 extern void external_stop_resolving_service(mDNSInterfaceID InterfaceID, const domainname *const fqdn, DNSServiceFlags flags);
 extern void external_connection_release(const domainname *instance);
 extern void external_start_resolving_service(mDNSInterfaceID InterfaceID, const domainname *const fqdn, DNSServiceFlags flags);
 extern void external_stop_resolving_service(mDNSInterfaceID InterfaceID, const domainname *const fqdn, DNSServiceFlags flags);
 extern void external_connection_release(const domainname *instance);
-#else
+
+#else   // APPLE_OSX_mDNSResponder
+
 #define external_start_browsing_for_service(A,B,C,D) (void)(A)
 #define external_stop_browsing_for_service(A,B,C,D)  (void)(A)
 #define external_start_advertising_service(A,B)      (void)(A)
 #define external_start_browsing_for_service(A,B,C,D) (void)(A)
 #define external_stop_browsing_for_service(A,B,C,D)  (void)(A)
 #define external_start_advertising_service(A,B)      (void)(A)
@@ -75,6 +83,7 @@ extern void external_connection_release(const domainname *instance);
 #define external_start_resolving_service(A,B,C)      (void)(A)
 #define external_stop_resolving_service(A,B,C)       (void)(A)
 #define external_connection_release(A)               (void)(A)
 #define external_start_resolving_service(A,B,C)      (void)(A)
 #define external_stop_resolving_service(A,B,C)       (void)(A)
 #define external_connection_release(A)               (void)(A)
+
 #endif // APPLE_OSX_mDNSResponder
 
 extern const char mDNSResponderVersionString_SCCS[];
 #endif // APPLE_OSX_mDNSResponder
 
 extern const char mDNSResponderVersionString_SCCS[];
index 475558e3ccf0a8ab483933637a8a9a526c7ff36f..e44865b80b59158393541787bf4c94f9065ae470 100755 (executable)
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 4 -*-
  *
 /* -*- Mode: C; tab-width: 4 -*-
  *
- * Copyright (c) 2009 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2009-2013 Apple Computer, 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.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -215,8 +215,8 @@ enum DNSSDError
     kDNSSDError_BadKey                    = -65561,
     kDNSSDError_Transient                 = -65562,
     kDNSSDError_ServiceNotRunning         = -65563,  /* Background daemon not running */
     kDNSSDError_BadKey                    = -65561,
     kDNSSDError_Transient                 = -65562,
     kDNSSDError_ServiceNotRunning         = -65563,  /* Background daemon not running */
-    kDNSSDError_NATPortMappingUnsupported = -65564,  /* NAT doesn't support NAT-PMP or UPnP */
-    kDNSSDError_NATPortMappingDisabled    = -65565,  /* NAT supports NAT-PMP or UPnP but it's disabled by the administrator */
+    kDNSSDError_NATPortMappingUnsupported = -65564,  /* NAT doesn't support PCP, NAT-PMP or UPnP */
+    kDNSSDError_NATPortMappingDisabled    = -65565,  /* NAT supports PCP, NAT-PMP or UPnP, but it's disabled by the administrator */
     kDNSSDError_NoRouter                  = -65566,  /* No router currently configured (probably no network connectivity) */
     kDNSSDError_PollingMode               = -65567
 } DNSSDError;
     kDNSSDError_NoRouter                  = -65566,  /* No router currently configured (probably no network connectivity) */
     kDNSSDError_PollingMode               = -65567
 } DNSSDError;
index 93ca80ce3ae10e1ba1fc845a0247e74ee043dd32..a57e4edf6ea35339532de3b2b3d1ae8814d5b9ba 100755 (executable)
       <AdditionalIncludeDirectories>..;$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r
     </ResourceCompile>\r
     <Link>\r
       <AdditionalIncludeDirectories>..;$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r
     </ResourceCompile>\r
     <Link>\r
-      <RegisterOutput>true</RegisterOutput>\r
+      <RegisterOutput>false</RegisterOutput>\r
       <AdditionalOptions>/NXCOMPAT /DYNAMICBASE %(AdditionalOptions)</AdditionalOptions>\r
       <AdditionalDependencies>ws2_32.lib;../../mDNSWindows/DLLStub/$(Platform)/$(Configuration)/dnssdStatic.lib;%(AdditionalDependencies)</AdditionalDependencies>\r
       <OutputFile>$(OutDir)dnssdX.dll</OutputFile>\r
       <AdditionalOptions>/NXCOMPAT /DYNAMICBASE %(AdditionalOptions)</AdditionalOptions>\r
       <AdditionalDependencies>ws2_32.lib;../../mDNSWindows/DLLStub/$(Platform)/$(Configuration)/dnssdStatic.lib;%(AdditionalDependencies)</AdditionalDependencies>\r
       <OutputFile>$(OutDir)dnssdX.dll</OutputFile>\r
index 5abd28b39f1fe050636c99fd1175789dd0107635..b5f254cd9c210d2f4a74ee5fca397776f2a5e465 100644 (file)
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 4 -*-
  *
 /* -*- Mode: C; tab-width: 4 -*-
  *
- * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2002-2013 Apple Computer, 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.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ mDNSlocal OSStatus MakeLsaStringFromUTF8String( PLSA_UNICODE_STRING output, cons
 mDNSlocal OSStatus MakeUTF8StringFromLsaString( char * output, size_t len, PLSA_UNICODE_STRING input );
 
 
 mDNSlocal OSStatus MakeUTF8StringFromLsaString( char * output, size_t len, PLSA_UNICODE_STRING input );
 
 
-BOOL
+mDNSBool
 LsaGetSecret( const char * inDomain, char * outDomain, unsigned outDomainSize, char * outKey, unsigned outKeySize, char * outSecret, unsigned outSecretSize )
 {
        PLSA_UNICODE_STRING             domainLSA;
 LsaGetSecret( const char * inDomain, char * outDomain, unsigned outDomainSize, char * outKey, unsigned outKeySize, char * outSecret, unsigned outSecretSize )
 {
        PLSA_UNICODE_STRING             domainLSA;
index 79643d6242f4c5ca011cc5bcc04f6fc98d1178c4..f5434f03f4d115ffd600ea0b04ed98d890b8446a 100644 (file)
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 4 -*-
  *
 /* -*- Mode: C; tab-width: 4 -*-
  *
- * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2002-2013 Apple Computer, 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.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@ extern "C" {
 
 
 extern mDNSBool
 
 
 extern mDNSBool
-LsaGetSecret( const char * inDomain, char * outDomain, unsigned outDomainLength, char * outKey, unsigned outKeyLength, char * outSecret, unsigned outSecretLength );
+LsaGetSecret( const char * inDomain, char * outDomain, unsigned outDomainSize, char * outKey, unsigned outKeySize, char * outSecret, unsigned outSecretSize );
 
 
 extern mDNSBool
 
 
 extern mDNSBool
@@ -39,4 +39,4 @@ LsaSetSecret( const char * inDomain, const char * inKey, const char * inSecret )
 #endif
 
 
 #endif
 
 
-#endif
\ No newline at end of file
+#endif
index fe0758cc9cc885889e522433c6975293c7699cc3..c0eb7a0f26d0fe23c8f29b9660df2f5174c492bd 100755 (executable)
@@ -249,6 +249,8 @@ xcopy /I/Y "$(TargetDir)$(TargetName).pdb"
     </PostBuildEvent>\r
   </ItemDefinitionGroup>\r
   <ItemGroup>\r
     </PostBuildEvent>\r
   </ItemDefinitionGroup>\r
   <ItemGroup>\r
+    <ClCompile Include="..\..\mDNSCore\anonymous.c" />\r
+    <ClCompile Include="..\..\mDNSCore\CryptoAlg.c" />\r
     <ClCompile Include="..\..\mDNSShared\DebugServices.c" />\r
     <ClCompile Include="..\..\mDNSCore\DNSCommon.c" />\r
     <ClCompile Include="..\..\mDNSCore\DNSDigest.c" />\r
     <ClCompile Include="..\..\mDNSShared\DebugServices.c" />\r
     <ClCompile Include="..\..\mDNSCore\DNSCommon.c" />\r
     <ClCompile Include="..\..\mDNSCore\DNSDigest.c" />\r
@@ -283,6 +285,8 @@ xcopy /I/Y "$(TargetDir)$(TargetName).pdb"
     </CustomBuild>\r
   </ItemGroup>\r
   <ItemGroup>\r
     </CustomBuild>\r
   </ItemGroup>\r
   <ItemGroup>\r
+    <ClInclude Include="..\..\mDNSCore\anonymous.h" />\r
+    <ClInclude Include="..\..\mDNSCore\CryptoAlg.h" />\r
     <ClInclude Include="..\..\mDNSShared\CommonServices.h" />\r
     <ClInclude Include="..\..\mDNSShared\DebugServices.h" />\r
     <ClInclude Include="..\..\mDNSCore\DNSCommon.h" />\r
     <ClInclude Include="..\..\mDNSShared\CommonServices.h" />\r
     <ClInclude Include="..\..\mDNSShared\DebugServices.h" />\r
     <ClInclude Include="..\..\mDNSCore\DNSCommon.h" />\r
index 0f3b7105a68c86c20d9e3731d48368c8371d0e3b..ba41b93bb49011b0f868aba0e2ef0f807ba8e27b 100755 (executable)
     <ClCompile Include="..\..\mDNSShared\uds_daemon.c">\r
       <Filter>Source Files</Filter>\r
     </ClCompile>\r
     <ClCompile Include="..\..\mDNSShared\uds_daemon.c">\r
       <Filter>Source Files</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="..\..\mDNSCore\anonymous.c">\r
+      <Filter>Source Files</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\..\mDNSCore\CryptoAlg.c">\r
+      <Filter>Source Files</Filter>\r
+    </ClCompile>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="..\..\mDNSShared\CommonServices.h">\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="..\..\mDNSShared\CommonServices.h">\r
     <ClInclude Include="..\..\mDNSShared\uds_daemon.h">\r
       <Filter>Header Files</Filter>\r
     </ClInclude>\r
     <ClInclude Include="..\..\mDNSShared\uds_daemon.h">\r
       <Filter>Header Files</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="..\..\mDNSCore\anonymous.h">\r
+      <Filter>Header Files</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="..\..\mDNSCore\CryptoAlg.h">\r
+      <Filter>Header Files</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ResourceCompile Include="Service.rc">\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ResourceCompile Include="Service.rc">\r
index 86dfa3aae732183959942a25ffaa48fa3869b046..73954e601e651ea028a876936975e5026dddd2b3 100755 (executable)
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 4 -*-
  *
 /* -*- Mode: C; tab-width: 4 -*-
  *
- * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2002-2013 Apple Computer, 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.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -673,6 +673,38 @@ mDNSexport mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, C
        return mDNSfalse;
        }
 
        return mDNSfalse;
        }
 
+mDNSexport void BumpDNSSECStats(mDNS *const m, DNSSECStatsAction action, DNSSECStatsType type, mDNSu32 value)
+    {
+    (void)m;
+    (void)action;
+    (void)type;
+    (void)value;
+    }
+
+// Proxy stub functions
+mDNSexport mDNSu8 *DNSProxySetAttributes(DNSQuestion *q, DNSMessageHeader *h, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *limit)
+{
+    (void) q;
+    (void) h;
+    (void) msg;
+    (void) ptr;
+    (void) limit;
+
+    return ptr;
+}
+
+mDNSexport void DNSProxyInit(mDNS *const m, mDNSu32 IpIfArr[], mDNSu32 OpIf)
+{
+    (void) m;
+    (void) IpIfArr;
+    (void) OpIf;
+}
+
+mDNSexport void DNSProxyTerminate(mDNS *const m)
+{
+    (void) m;
+}
+
 //===========================================================================================================================
 //     mDNSPlatformMemZero
 //===========================================================================================================================
 //===========================================================================================================================
 //     mDNSPlatformMemZero
 //===========================================================================================================================
@@ -1419,6 +1451,13 @@ exit:
 }
 
 
 }
 
 
+mDNSexport mDNSBool mDNSPlatformPeekUDP(mDNS *const m, UDPSocket *src)
+{
+       DEBUG_UNUSED( m );
+       DEBUG_UNUSED( src );
+       return mDNSfalse;
+}
+
 mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID InterfaceID)
        {
        DEBUG_UNUSED( m );
 mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID InterfaceID)
        {
        DEBUG_UNUSED( m );
@@ -1648,8 +1687,10 @@ mDNSPlatformTLSTearDownCerts(void)
 mDNSlocal void SetDNSServers( mDNS *const m );
 mDNSlocal void SetSearchDomainList( void );
 
 mDNSlocal void SetDNSServers( mDNS *const m );
 mDNSlocal void SetSearchDomainList( void );
 
-mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **regDomains, DNameListElem **browseDomains)
+mDNSexport mDNSBool mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **regDomains, DNameListElem **browseDomains, mDNSBool ackConfig)
 {
 {
+       (void) ackConfig;
+
        if (setservers) SetDNSServers(m);
        if (setsearch) SetSearchDomainList();
        
        if (setservers) SetDNSServers(m);
        if (setsearch) SetSearchDomainList();
        
@@ -1667,6 +1708,7 @@ mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDN
        {
                GetDDNSDomains( regDomains, kServiceParametersNode TEXT("\\DynDNS\\Setup\\") kServiceDynDNSRegistrationDomains );
        }
        {
                GetDDNSDomains( regDomains, kServiceParametersNode TEXT("\\DynDNS\\Setup\\") kServiceDynDNSRegistrationDomains );
        }
+    return mDNStrue;
 }
 
 
 }
 
 
@@ -1920,7 +1962,7 @@ SetDNSServers( mDNS *const m )
        {
                mDNSAddr addr;
                err = StringToAddress( &addr, ipAddr->IpAddress.String );
        {
                mDNSAddr addr;
                err = StringToAddress( &addr, ipAddr->IpAddress.String );
-               if ( !err ) mDNS_AddDNSServer(m, mDNSNULL, mDNSInterface_Any, &addr, UnicastDNSPort, mDNSfalse, DEFAULT_UDNS_TIMEOUT, mDNSfalse, 0);
+               if ( !err ) mDNS_AddDNSServer(m, mDNSNULL, mDNSInterface_Any, 0, &addr, UnicastDNSPort, kScopeNone, DEFAULT_UDNS_TIMEOUT, mDNSfalse, 0, mDNStrue, mDNStrue, mDNSfalse);
        }
 
 exit:
        }
 
 exit:
@@ -2113,6 +2155,18 @@ mDNSexport void mDNSPlatformSendKeepalive(mDNSAddr *sadd, mDNSAddr *dadd, mDNSIP
        (void) win;             // Unused
        }
 
        (void) win;             // Unused
        }
 
+mDNSexport mStatus mDNSPlatformGetRemoteMacAddr(mDNSAddr *raddr, char *eth)
+       {
+       (void) raddr; // Unused
+       (void) eth;   // Unused
+       }
+
+mDNSexport  mStatus    mDNSPlatformStoreSPSMACAddr(mDNSAddr *spsaddr, char *ifname)
+       {
+       (void) spsaddr; // Unused
+       (void) ifname;  // Unused
+       }
+
 mDNSexport mStatus mDNSPlatformRetrieveTCPInfo(mDNS *const m, mDNSAddr *laddr, mDNSIPPort *lport, mDNSAddr *raddr, mDNSIPPort *rport, mDNSTCPInfo *mti)
        {
        (void) m;       // Unused
 mDNSexport mStatus mDNSPlatformRetrieveTCPInfo(mDNS *const m, mDNSAddr *laddr, mDNSIPPort *lport, mDNSAddr *raddr, mDNSIPPort *rport, mDNSTCPInfo *mti)
        {
        (void) m;       // Unused
@@ -2123,6 +2177,46 @@ mDNSexport mStatus mDNSPlatformRetrieveTCPInfo(mDNS *const m, mDNSAddr *laddr, m
        (void) mti;     // Unused
        }
 
        (void) mti;     // Unused
        }
 
+mDNSexport mDNSBool mDNSPlatformAllowPID(mDNS *const m, DNSQuestion *q)
+    {
+    (void) m;
+    (void) q;
+    return mDNStrue;
+    }
+
+mDNSexport mDNSs32 mDNSPlatformGetServiceID(mDNS *const m, DNSQuestion *q)
+    {
+    (void) m;
+    (void) q;
+    return 0;
+    }
+
+mDNSexport void mDNSPlatformSetDelegatePID(UDPSocket *src, const mDNSAddr *dst, DNSQuestion *q)
+    {
+    (void) src;
+    (void) dst;
+    (void) q;
+    }
+
+mDNSexport mDNSs32 mDNSPlatformGetPID()
+    {
+    return 0;
+    }
+
+mDNSexport mDNSu16 mDNSPlatformGetUDPPort(UDPSocket *sock)
+{
+       DEBUG_UNUSED( sock );
+       return (mDNSu16)-1;
+}
+
+mDNSexport mDNSBool mDNSPlatformInterfaceIsD2D(mDNSInterfaceID InterfaceID)
+{
+       DEBUG_UNUSED( InterfaceID );
+    
+       return mDNSfalse;
+}
+
 #if 0
 #pragma mark -
 #endif
 #if 0
 #pragma mark -
 #endif