]> git.saurik.com Git - apple/mdnsresponder.git/commitdiff
mDNSResponder-1096.40.7.tar.gz macos-10151 v1096.40.7
authorApple <opensource@apple.com>
Thu, 28 May 2020 16:02:55 +0000 (16:02 +0000)
committerApple <opensource@apple.com>
Thu, 28 May 2020 16:02:55 +0000 (16:02 +0000)
31 files changed:
Clients/dnssdutil/dnssdutil.c
Makefile
mDNSCore/DNSCommon.c
mDNSCore/mDNS.c
mDNSCore/mDNSEmbeddedAPI.h
mDNSMacOSX/ApplePlatformFeatures.h
mDNSMacOSX/Tests/BATS Scripts/bats_test_proxy.sh
mDNSMacOSX/Tests/CNameRecordTest.m [deleted file]
mDNSMacOSX/Tests/DNSMessageTest.m [deleted file]
mDNSMacOSX/Tests/HelperFunctionTest.m [deleted file]
mDNSMacOSX/Tests/Info.plist [deleted file]
mDNSMacOSX/Tests/LocalOnlyTimeoutTest.m [deleted file]
mDNSMacOSX/Tests/ResourceRecordTest.m [deleted file]
mDNSMacOSX/Tests/Unit Tests/CNameRecordTest.m [new file with mode: 0644]
mDNSMacOSX/Tests/Unit Tests/CacheOrderTest.m [new file with mode: 0644]
mDNSMacOSX/Tests/Unit Tests/DNSMessageTest.m [new file with mode: 0644]
mDNSMacOSX/Tests/Unit Tests/HelperFunctionTest.m [new file with mode: 0644]
mDNSMacOSX/Tests/Unit Tests/Info.plist [new file with mode: 0644]
mDNSMacOSX/Tests/Unit Tests/LocalOnlyTimeoutTest.m [new file with mode: 0644]
mDNSMacOSX/Tests/Unit Tests/LocalOnlyWithInterfacesTest.m [new file with mode: 0644]
mDNSMacOSX/Tests/Unit Tests/ResourceRecordTest.m [new file with mode: 0644]
mDNSMacOSX/Tests/Unit Tests/SuspiciousReplyTest.m [new file with mode: 0644]
mDNSMacOSX/Tests/Unit Tests/mDNSCoreReceiveTest.m [new file with mode: 0644]
mDNSMacOSX/Tests/mDNSCoreReceiveTest.m [deleted file]
mDNSMacOSX/Tests/mDNSResponder.plist
mDNSMacOSX/mDNSMacOSX.c
mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj
mDNSShared/dns_sd.h
mDNSShared/uds_daemon.c
unittests/unittest_common.c
unittests/unittest_common.h

index ef67036c925f8721698edb12b75ee46edc3f342d..5ab85965956168a1ddfa3470b3dae39c14af9484 100644 (file)
@@ -1973,7 +1973,7 @@ static CLIOption          kXPCSendOpts[] =
 };
 #endif // TARGET_OS_DARWIN
 
-#if ( MDNSRESPONDER_PROJECT )
+#if( MDNSRESPONDER_PROJECT )
 //===========================================================================================================================
 //     InterfaceMonitor
 //===========================================================================================================================
@@ -2002,6 +2002,7 @@ static CLIOption          kDNSProxyOpts[] =
        StringOption(      'o', "outputInterface", &gDNSProxy_OutputInterface, "name or index", "Interface to forward queries over. Use '0' for primary interface. (default: 0)", false ),
        CLI_OPTION_END()
 };
+
 #endif // MDNSRESPONDER_PROJECT
 
 //===========================================================================================================================
@@ -2070,7 +2071,7 @@ static CLIOption          kGlobalOpts[] =
        Command( "dnsconfig",                   NULL,                                   kDNSConfigOpts,                 "Add/remove a supplemental resolver entry to/from the system's DNS configuration.", true ),
        Command( "xpcsend",                             XPCSendCmd,                             kXPCSendOpts,                   "Sends a message to an XPC service.", true ),
 #endif
-#if ( MDNSRESPONDER_PROJECT )
+#if( MDNSRESPONDER_PROJECT )
        Command( "interfaceMonitor",    InterfaceMonitorCmd,    kInterfaceMonitorOpts,  "mDNSResponder's interface monitor.", true ),
        Command( "dnsproxy",                    DNSProxyCmd,                    kDNSProxyOpts,                  "Enables mDNSResponder's DNS proxy.", true ),
 #endif
@@ -14520,6 +14521,7 @@ static Boolean expensiveConstrainedEndsWith( const char *str, const char *suffix
 //===========================================================================================================================
 //    ExpensiveConstrainedTestCmd
 //===========================================================================================================================
+
 static void ExpensiveConstrainedTestCmd( void )
 {
     OSStatus                        err;
@@ -15356,7 +15358,7 @@ static Boolean expensiveConstrainedEndsWith( const char *str, const char *suffix
 }
 
 //===========================================================================================================================
-//    RegistrationTestCmd
+//     RegistrationTestCmd
 //===========================================================================================================================
 
 typedef struct RegistrationSubtest             RegistrationSubtest;
index 4e38d77a9823ecc2aa6a1b882dc0bae9858b7307..88f994207d164425c31c2ac794a2b9cb5811e637 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -17,7 +17,7 @@
 
 include $(MAKEFILEPATH)/pb_makefiles/platform.make
 
-MVERS = "mDNSResponder-1096.0.2"
+MVERS = "mDNSResponder-1096.40.7"
 
 VER =
 ifneq ($(strip $(GCC_VERSION)),)
index 2c4e97e9f2aaf80af14e1feff7c0a20dd13e8bdb..1e7088383098f3f81cc56fecc58aab54f05e83d1 100644 (file)
@@ -1827,7 +1827,7 @@ mDNSexport mDNSBool LocalOnlyRecordAnswersQuestion(AuthRecord *const ar, const D
     if (rr->InterfaceID &&
         q->InterfaceID != mDNSInterface_LocalOnly &&
         ((q->InterfaceID && rr->InterfaceID != q->InterfaceID) ||
-        (!q->InterfaceID && rr->InterfaceID != mDNSInterface_LocalOnly))) return(mDNSfalse);
+        (!q->InterfaceID && !LocalOnlyOrP2PInterface(rr->InterfaceID)))) return(mDNSfalse);
 
     // Entries in /etc/hosts are added as LocalOnly resource records. The LocalOnly resource records
     // may have a scope e.g., fe80::1%en0. The question may be scoped or not: the InterfaceID may be set
index 4ec1934f365e4735956942bbf4f2d26ca4b21db0..2fbea041248d90156654a91d42e2b1e3e615f892 100755 (executable)
@@ -7357,8 +7357,7 @@ mDNSlocal const AuthRecord *FindRRSet(const mDNS *const m, const CacheRecord *co
     {
         if (IdenticalResourceRecord(&rr->resrec, &pktrr->resrec))
         {
-            while (rr->RRSet && rr != rr->RRSet) rr = rr->RRSet;
-            return(rr);
+            return(rr->RRSet ? rr->RRSet : rr);
         }
     }
     return(mDNSNULL);
@@ -8140,7 +8139,18 @@ mDNSlocal DNSQuestion *ExpectingUnicastResponseForQuestion(const mDNS *const m,
             if (mDNSSameOpaque16(q->TargetQID, id)) return(q);
             else
             {
-                if (!tcp && suspiciousQ) *suspiciousQ = q;
+                // Everything but the QIDs match up, should we be suspicious?
+                if (!tcp && suspiciousQ)
+                {
+#if MDNSRESPONDER_SUPPORTS(APPLE, SUSPICIOUS_REPLY_DEFENSE)
+                    if (mDNSSameOpaque16(q->LastTargetQID, id)) // Only be suspicious if this is also not the "last" used QID (on a DNS server set change)
+                    {
+                        LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, "Ignored but not suspicious LastTargetQID %d TargetQID %d", mDNSVal16(q->LastTargetQID), mDNSVal16(q->TargetQID));
+                    }
+                    else
+#endif
+                    *suspiciousQ = q;
+                }
                 return(mDNSNULL);
             }
         }
@@ -8281,6 +8291,25 @@ mDNSexport CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, C
     return(rr);
 }
 
+mDNSlocal void RefreshCacheRecordCacheGroupOrder(CacheGroup *cg, CacheRecord *cr)
+{   //  Move the cache record to the tail of the cache group to maintain a fresh ordering
+    if (cg->rrcache_tail != &cr->next)          // If not already at the tail
+    {
+        CacheRecord **rp;
+        for (rp = &cg->members; *rp; rp = &(*rp)->next)
+        {
+            if (*rp == cr)                      // This item points to this record
+            {
+                *rp = cr->next;                 // Remove this record
+                break;
+            }
+        }
+        cr->next = mDNSNULL;                    // This record is now last
+        *(cg->rrcache_tail) = cr;               // Append this record to tail of cache group
+        cg->rrcache_tail = &(cr->next);         // Advance tail pointer
+    }
+}
+
 mDNSlocal void RefreshCacheRecord(mDNS *const m, CacheRecord *rr, mDNSu32 ttl)
 {
     rr->TimeRcvd             = m->timenow;
@@ -8862,6 +8891,9 @@ mDNSexport CacheRecord* mDNSCoreReceiveCacheCheck(mDNS *const m, const DNSMessag
 
                 if (cr->resrec.rroriginalttl == 0) debugf("uDNS rescuing %s", CRDisplayString(m, cr));
                 RefreshCacheRecord(m, cr, m->rec.r.resrec.rroriginalttl);
+                // RefreshCacheRecordCacheGroupOrder will modify the cache group member list that is currently being iterated over in this for-loop.
+                // It is safe to call because the else-if body will unconditionally break out of the for-loop now that it has found the entry to update.
+                RefreshCacheRecordCacheGroupOrder(cg, cr);
                 cr->responseFlags = response->h.flags;
 
                 // If we may have NSEC records returned with the answer (which we don't know yet as it
@@ -8898,7 +8930,7 @@ mDNSexport CacheRecord* mDNSCoreReceiveCacheCheck(mDNS *const m, const DNSMessag
                         }
                     }
                 }
-                break;
+                break;  // Check usage of RefreshCacheRecordCacheGroupOrder before removing (See note above)
             }
             else
             {
@@ -12024,6 +12056,9 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu
 
 #ifndef UNICAST_DISABLED
     question->TargetQID = Question_uDNS(question) ? mDNS_NewMessageID(m) : zeroID;
+#if MDNSRESPONDER_SUPPORTS(APPLE, SUSPICIOUS_REPLY_DEFENSE)
+    question->LastTargetQID = zeroID;
+#endif
 #else
     question->TargetQID = zeroID;
 #endif
@@ -12570,13 +12605,30 @@ mDNSlocal void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatu
 mDNSlocal void mDNS_RandomizedHostNameCallback(mDNS *m, AuthRecord *rr, mStatus result);
 #endif
 
-mDNSlocal NetworkInterfaceInfo *FindFirstAdvertisedInterface(mDNS *const m)
+mDNSlocal AuthRecord *GetInterfaceAddressRecord(NetworkInterfaceInfo *intf, mDNSBool forRandHostname)
+{
+#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME)
+        return(forRandHostname ? &intf->RR_AddrRand : &intf->RR_A);
+#else
+        (void)forRandHostname; // Unused.
+        return(&intf->RR_A);
+#endif
+}
+
+mDNSlocal AuthRecord *GetFirstAddressRecordEx(const mDNS *const m, const mDNSBool forRandHostname)
 {
     NetworkInterfaceInfo *intf;
     for (intf = m->HostInterfaces; intf; intf = intf->next)
-        if (intf->Advertise) break;
-    return(intf);
+    {
+        if (!intf->Advertise) continue;
+#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME)
+        if (mDNSPlatformInterfaceIsAWDL(intf->InterfaceID)) continue;
+#endif
+        return(GetInterfaceAddressRecord(intf, forRandHostname));
+    }
+    return(mDNSNULL);
 }
+#define GetFirstAddressRecord(M)    GetFirstAddressRecordEx(M, mDNSfalse)
 
 // The parameter "set" here refers to the set of AuthRecords used to advertise this interface.
 // (It's a set of records, not a set of interfaces.)
@@ -12586,7 +12638,6 @@ mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNS
 mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set)
 #endif
 {
-    NetworkInterfaceInfo *primary;
     const domainname *hostname;
     mDNSRecordCallback *hostnameCallback;
     AuthRecord *addrAR;
@@ -12676,22 +12727,15 @@ mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set)
         ptrAR->ForceMCast = mDNStrue;           // This PTR points to our dot-local name, so don't ever try to write it into a uDNS server
     }
 
-    primary = FindFirstAdvertisedInterface(m);
-    if (!primary) primary = set; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary
-    // We should never have primary be NULL, because even if there is
-    // no other interface yet, we should always find ourself in the list.
-
 #if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME)
-    if (!interfaceIsAWDL && useRandomizedHostname)
-    {
-        addrAR->RRSet = &primary->RR_AddrRand;
-    }
-    else
+    addrAR->RRSet = interfaceIsAWDL ? addrAR : GetFirstAddressRecordEx(m, useRandomizedHostname);
+#else
+    addrAR->RRSet = GetFirstAddressRecord(m);
 #endif
-    {
-        addrAR->RRSet = &primary->RR_A;
-    }
+    if (!addrAR->RRSet) addrAR->RRSet = addrAR;
     mDNS_Register_internal(m, addrAR);
+    LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, "Initialized RRSet for " PRI_S, ARDisplayString(m, addrAR));
+    LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, "RRSet:                " PRI_S, ARDisplayString(m, addrAR->RRSet));
     if (ptrAR) mDNS_Register_internal(m, ptrAR);
 
 #if MDNSRESPONDER_SUPPORTS(APPLE, D2D)
@@ -13171,6 +13215,36 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s
     return(mStatus_NoError);
 }
 
+mDNSlocal void AdjustAddressRecordSetsEx(mDNS *const m, NetworkInterfaceInfo *removedIntf, mDNSBool forRandHostname)
+{
+    NetworkInterfaceInfo *intf;
+    const AuthRecord *oldAR;
+    AuthRecord *newAR;
+#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME)
+    if (mDNSPlatformInterfaceIsAWDL(removedIntf->InterfaceID)) return;
+#endif
+    oldAR = GetInterfaceAddressRecord(removedIntf, forRandHostname);
+    newAR = GetFirstAddressRecordEx(m, forRandHostname);
+    for (intf = m->HostInterfaces; intf; intf = intf->next)
+    {
+        AuthRecord *ar;
+#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME)
+        if (mDNSPlatformInterfaceIsAWDL(intf->InterfaceID)) continue;
+#endif
+        ar = GetInterfaceAddressRecord(intf, forRandHostname);
+        if (ar->RRSet == oldAR)
+        {
+            ar->RRSet = newAR ? newAR : ar;
+            LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, "Changed RRSet for " PRI_S, ARDisplayString(m, ar));
+            LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, "New RRSet:        " PRI_S, ARDisplayString(m, ar->RRSet));
+        }
+    }
+}
+#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME)
+#define AdjustAddressRecordSetsForRandHostname(M, REMOVED_INTF) AdjustAddressRecordSetsEx(M, REMOVED_INTF, mDNStrue)
+#endif
+#define AdjustAddressRecordSets(M, REMOVED_INTF)                AdjustAddressRecordSetsEx(M, REMOVED_INTF, mDNSfalse)
+
 // Note: mDNS_DeregisterInterface 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.
@@ -13181,9 +13255,7 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se
 #endif
     NetworkInterfaceInfo **p = &m->HostInterfaces;
     mDNSBool revalidate = mDNSfalse;
-    NetworkInterfaceInfo *primary;
     NetworkInterfaceInfo *intf;
-    AuthRecord *A;
 
     mDNS_Lock(m);
 
@@ -13291,12 +13363,11 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se
 
     // If we still have address records referring to this one, update them.
     // This is safe, because this NetworkInterfaceInfo has already been unlinked from the list,
-    // so the call to FindFirstAdvertisedInterface() won’t accidentally find it.
-    primary = FindFirstAdvertisedInterface(m);
-    A = primary ? &primary->RR_A : mDNSNULL;
-    for (intf = m->HostInterfaces; intf; intf = intf->next)
-        if (intf->RR_A.RRSet == &set->RR_A)
-            intf->RR_A.RRSet = A;
+    // so the call to AdjustAddressRecordSets*() won’t accidentally find it.
+    AdjustAddressRecordSets(m, set);
+#if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME)
+    AdjustAddressRecordSetsForRandHostname(m, set);
+#endif
 
     // If we were advertising on this interface, deregister those address and reverse-lookup records now
     if (set->Advertise) DeadvertiseInterface(m, set, kDeadvertiseFlag_All);
@@ -14838,6 +14909,10 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m)
         LogInfo("uDNS_SetupDNSConfig: No configuration change");
         return mStatus_NoError;
     }
+#if MDNSRESPONDER_SUPPORTS(APPLE, SUSPICIOUS_REPLY_DEFENSE)
+    // Reset suspicious mode on any DNS configuration change
+    m->NextSuspiciousTimeout = 0;
+#endif
 
     // 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
@@ -14957,6 +15032,9 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m)
 
         q->Suppressed = ShouldSuppressUnicastQuery(q, s);
         q->unansweredQueries = 0;
+#if MDNSRESPONDER_SUPPORTS(APPLE, SUSPICIOUS_REPLY_DEFENSE)
+        q->LastTargetQID = q->TargetQID;
+#endif
         q->TargetQID = mDNS_NewMessageID(m);
         if (!q->Suppressed) ActivateUnicastQuery(m, q, mDNStrue);
     }
index 041772a5c0a8e2d0761f7c3ad759b92e8b5a1c5d..78d957ba46e1d54e2b2ba2720cb7eeba8820a501 100755 (executable)
@@ -2052,6 +2052,9 @@ struct DNSQuestion_struct
     mDNSQuestionCallback *QuestionCallback;
     mDNSQuestionResetHandler ResetHandler;
     void                 *QuestionContext;
+#if MDNSRESPONDER_SUPPORTS(APPLE, SUSPICIOUS_REPLY_DEFENSE)
+    mDNSOpaque16 LastTargetQID;             // Last used QID, to help determine suspicion with valid in-flight replies.
+#endif
 #if MDNSRESPONDER_SUPPORTS(APPLE, METRICS)
     uDNSMetrics metrics;                    // Data used for collecting unicast DNS query metrics.
 #endif
@@ -2538,7 +2541,7 @@ extern const mDNSInterfaceID mDNSInterface_P2P;             // Special value
 extern const mDNSInterfaceID uDNSInterfaceMark;             // Special value
 extern const mDNSInterfaceID mDNSInterface_BLE;             // Special value
 
-#define LocalOnlyOrP2PInterface(INTERFACE)  ((INTERFACE == mDNSInterface_LocalOnly) || (INTERFACE == mDNSInterface_P2P) || (INTERFACE == mDNSInterface_BLE))
+#define LocalOnlyOrP2PInterface(INTERFACE)  (((INTERFACE) == mDNSInterface_LocalOnly) || ((INTERFACE) == mDNSInterface_P2P) || ((INTERFACE) == mDNSInterface_BLE))
 
 extern const mDNSIPPort DiscardPort;
 extern const mDNSIPPort SSHPort;
@@ -3636,13 +3639,13 @@ struct CompileTimeAssertionChecks_mDNS
     char sizecheck_AuthRecord          [(sizeof(AuthRecord)           <=  1168) ? 1 : -1];
     char sizecheck_CacheRecord         [(sizeof(CacheRecord)          <=   232) ? 1 : -1];
     char sizecheck_CacheGroup          [(sizeof(CacheGroup)           <=   232) ? 1 : -1];
-    char sizecheck_DNSQuestion         [(sizeof(DNSQuestion)          <=  1128) ? 1 : -1];
+    char sizecheck_DNSQuestion         [(sizeof(DNSQuestion)          <=  1136) ? 1 : -1];
 
     char sizecheck_ZoneData            [(sizeof(ZoneData)             <=  2000) ? 1 : -1];
     char sizecheck_NATTraversalInfo    [(sizeof(NATTraversalInfo)     <=   200) ? 1 : -1];
     char sizecheck_HostnameInfo        [(sizeof(HostnameInfo)         <=  3050) ? 1 : -1];
     char sizecheck_DNSServer           [(sizeof(DNSServer)            <=   328) ? 1 : -1];
-    char sizecheck_NetworkInterfaceInfo[(sizeof(NetworkInterfaceInfo) <=  8240) ? 1 : -1];
+    char sizecheck_NetworkInterfaceInfo[(sizeof(NetworkInterfaceInfo) <=  8272) ? 1 : -1];
     char sizecheck_ServiceRecordSet    [(sizeof(ServiceRecordSet)     <=  4728) ? 1 : -1];
     char sizecheck_DomainAuthInfo      [(sizeof(DomainAuthInfo)       <=   944) ? 1 : -1];
 #if APPLE_OSX_mDNSResponder
index 77f3eab589bfd76da6f46f67419dcdc865bd5d0d..a4586af6b64359c8ee9ae51c0a977e5931bef1aa 100644 (file)
     #endif
 #endif
 
+// Feature: No system wake for network access.
+// Radar:   <rdar://problem/28079659&55038229>
+// Enabled: Yes, but only for iOS and watchOS, which shouldn't act as sleep-proxy clients.
+
+#if !defined(MDNSRESPONDER_SUPPORTS_APPLE_NO_WAKE_FOR_NET_ACCESS)
+    #if (!defined(TARGET_OS_IOS) || !defined(TARGET_OS_WATCH))
+        #error "Expected TARGET_OS_IOS and TARGET_OS_WATCH to be defined."
+    #endif
+    #if (TARGET_OS_IOS || TARGET_OS_WATCH)
+        #define MDNSRESPONDER_SUPPORTS_APPLE_NO_WAKE_FOR_NET_ACCESS 1
+    #else
+        #define MDNSRESPONDER_SUPPORTS_APPLE_NO_WAKE_FOR_NET_ACCESS 0
+    #endif
+#endif
+
 // Feature: Support for having finer granularity of log redaction, by using os_log based-log routine.
 // Radar:   <rdar://problem/42814956>
 // Enabled: Yes.
index 03e0bdf9e3d2561ef9daa6260e434c7861764da6..c1bcebd03e30209a5772e5d39e03a747a58c7153 100755 (executable)
@@ -47,7 +47,7 @@ function test_proxy_on {
     local dnssdutil_pid=$!
 
     # See if that worked
-    sleep 1
+    sleep 5
     local dnssdutil_pid_now=$(ps xa |sed -n -e 's/^ *\([0-9][0-9]*\).*$/\1/' -e "/$dnssdutil_pid/p")
     if [ $dnssdutil_pid != "$dnssdutil_pid_now" ]; then
         echo "Failed to enable DNS proxy $dnssdutil_pid $dnssdutil_pid_now."
diff --git a/mDNSMacOSX/Tests/CNameRecordTest.m b/mDNSMacOSX/Tests/CNameRecordTest.m
deleted file mode 100644 (file)
index 873561d..0000000
+++ /dev/null
@@ -1,368 +0,0 @@
-/*
- * Copyright (c) 2017-2019 Apple Inc. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "unittest_common.h"
-#import <XCTest/XCTest.h>
-
-struct UDPSocket_struct
-{
-       mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port
-};
-typedef struct UDPSocket_struct UDPSocket;
-
-// This client request was generated using the following command: "dns-sd -Q 123server.dotbennu.com. A".
-uint8_t query_client_msgbuf[35] = {
-       0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x32, 0x33, 0x73, 0x65, 0x72, 0x76, 0x65,
-       0x72, 0x2e, 0x64, 0x6f, 0x74, 0x62, 0x65, 0x6e, 0x6e, 0x75, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00,
-       0x01, 0x00, 0x01
-};
-
-// This uDNS message is a canned response that was originally captured by wireshark.
-uint8_t query_response_msgbuf[108] = {
-    0x69, 0x41, // transaction id
-       0x85, 0x80, // flags
-       0x00, 0x01, // 1 question for 123server.dotbennu.com. Addr
-       0x00, 0x02,     // 2 anwsers: 123server.dotbennu.com. CNAME test212.dotbennu.com., test212.dotbennu.com. Addr 10.100.0.1,
-       0x00, 0x01,     // 1 authorities anwser: dotbennu.com. NS cardinal2.apple.com.
-       0x00, 0x00, 0x09, 0x31, 0x32, 0x33,
-    0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x08, 0x64, 0x6f, 0x74, 0x62, 0x65, 0x6e, 0x6e, 0x75, 0x03,
-    0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00,
-    0x02, 0x56, 0x00, 0x0a, 0x07, 0x74, 0x65, 0x73, 0x74, 0x32, 0x31, 0x32, 0xc0, 0x16, 0xc0, 0x34,
-    0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x04, 0x0a, 0x64, 0x00, 0x01, 0xc0, 0x16,
-    0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0x51, 0x80, 0x00, 0x12, 0x09, 0x63, 0x61, 0x72, 0x64, 0x69,
-    0x6e, 0x61, 0x6c, 0x32, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x65, 0xc0, 0x1f
-};
-
-// Variables associated with contents of the above uDNS message
-#define uDNS_TargetQID 16745
-char udns_original_domainname_cstr[] = "123server.dotbennu.com.";
-char udns_cname_domainname_cstr[] = "test212.dotbennu.com.";
-//static const mDNSv4Addr dns_response_ipv4 = {{ 10, 100, 0, 1 }};
-
-@interface CNameRecordTest : XCTestCase
-{
-    UDPSocket* local_socket;
-    request_state* client_request_message;}
-@end
-
-@implementation CNameRecordTest
-
-// The InitThisUnitTest() initializes the mDNSResponder environment as well as
-// a DNSServer. It also allocates memory for a local_socket and client request.
-// Note: This unit test does not send packets on the wire and it does not open sockets.
-- (void)setUp
-{
-    // Init unit test environment and verify no error occurred.
-    mStatus result = init_mdns_environment(mDNStrue);
-    XCTAssertEqual(result, mStatus_NoError);
-    
-    // Add one DNS server and verify it was added.
-    AddDNSServer_ut();
-    XCTAssertEqual(CountOfUnicastDNSServers(&mDNSStorage), 1);
-    
-    // Create memory for a socket that is never used or opened.
-    local_socket = (UDPSocket *) mDNSPlatformMemAllocateClear(sizeof(*local_socket));
-    
-    // Create memory for a request that is used to make this unit test's client request.
-    client_request_message = calloc(1, sizeof(request_state));
-}
-
-- (void)tearDown
-{
-    mDNS *m = &mDNSStorage;
-    request_state* req = client_request_message;
-    DNSServer   *ptr, **p = &m->DNSServers;
-    
-    while (req->replies)
-    {
-        reply_state *reply = req->replies;
-        req->replies = req->replies->next;
-        mDNSPlatformMemFree(reply);
-    }
-    mDNSPlatformMemFree(req);
-    
-    mDNSPlatformMemFree(local_socket);
-    
-    while (*p)
-    {
-        ptr = *p;
-        *p = (*p)->next;
-        LogInfo("FinalizeUnitTest: Deleting server %p %#a:%d (%##s)", ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c);
-        mDNSPlatformMemFree(ptr);
-    }
-}
-
-// This test simulates a uds client request by setting up a client request and then
-// calling mDNSResponder's handle_client_request.  The handle_client_request function
-// processes the request and starts a query.  This unit test verifies
-// the client request and query were setup as expected.  This unit test also calls
-// mDNS_execute which determines the cache does not contain the new question's
-// answer.
-- (void)testStartClientQueryRequest
-{
-    mDNS *const m = &mDNSStorage;
-    request_state* req = client_request_message;
-    char *msgptr = (char *)query_client_msgbuf;
-    size_t msgsz = sizeof(query_client_msgbuf);
-    mDNSs32 min_size = sizeof(DNSServiceFlags) + sizeof(mDNSu32) + 4;
-    DNSQuestion *q;
-    mStatus err = mStatus_NoError;
-    char qname_cstr[MAX_ESCAPED_DOMAIN_NAME];
-    
-    // Process the unit test's client request
-    start_client_request(req, msgptr, msgsz, query_request, local_socket);
-    XCTAssertEqual(err, mStatus_NoError);
-    
-    // Verify the request fields were set as expected
-    XCTAssertNil((__bridge id)req->next);
-    XCTAssertNil((__bridge id)req->primary);
-    XCTAssertEqual(req->sd, client_req_sd);
-    XCTAssertEqual(req->process_id, client_req_process_id);
-    XCTAssertFalse(strcmp(req->pid_name, client_req_pid_name));
-    XCTAssertEqual(req->validUUID, mDNSfalse);
-    XCTAssertEqual(req->errsd, 0);
-    XCTAssertEqual(req->uid, client_req_uid);
-    XCTAssertEqual(req->ts, t_complete);
-    XCTAssertGreaterThan((mDNSs32)req->data_bytes, min_size);
-    XCTAssertEqual(req->msgend, msgptr+msgsz);
-    XCTAssertNil((__bridge id)(void*)req->msgbuf);
-    XCTAssertEqual(req->hdr.version, VERSION);
-    XCTAssertNil((__bridge id)req->replies);
-    XCTAssertNotEqual(req->terminate, (req_termination_fn)0);
-    XCTAssertEqual(req->flags, kDNSServiceFlagsReturnIntermediates);
-    XCTAssertEqual(req->interfaceIndex, kDNSServiceInterfaceIndexAny);
-    
-    // Verify the query fields were set as expected
-    q = &req->u.queryrecord.op.q;
-    XCTAssertNotEqual(q, (DNSQuestion *)mDNSNULL);
-    XCTAssertEqual(q, m->Questions);
-    XCTAssertEqual(q, m->NewQuestions);
-    XCTAssertEqual(q->SuppressUnusable, mDNSfalse);
-    XCTAssertEqual(q->ReturnIntermed, mDNStrue);
-    XCTAssertEqual(q->Suppressed, mDNSfalse);
-    
-    ConvertDomainNameToCString(&q->qname, qname_cstr);
-    XCTAssertFalse(strcmp(qname_cstr, udns_original_domainname_cstr));
-    XCTAssertEqual(q->qnamehash, DomainNameHashValue(&q->qname));
-    
-    XCTAssertEqual(q->InterfaceID, mDNSInterface_Any);
-    XCTAssertEqual(q->flags, req->flags);
-    XCTAssertEqual(q->qtype, 1);
-    XCTAssertEqual(q->qclass, 1);
-    XCTAssertEqual(q->LongLived, 0);
-    XCTAssertEqual(q->ExpectUnique, mDNSfalse);
-    XCTAssertEqual(q->ForceMCast, 0);
-    XCTAssertEqual(q->TimeoutQuestion, 0);
-    XCTAssertEqual(q->WakeOnResolve, 0);
-    XCTAssertEqual(q->UseBackgroundTraffic, 0);
-    XCTAssertEqual(q->ValidationRequired, 0);
-    XCTAssertEqual(q->ValidatingResponse, 0);
-    XCTAssertEqual(q->ProxyQuestion, 0);
-    XCTAssertNotEqual((void*)q->QuestionCallback, (void*)mDNSNULL);
-    XCTAssertNil((__bridge id)q->DNSSECAuthInfo);
-    XCTAssertNil((__bridge id)(void*)q->DAIFreeCallback);
-    XCTAssertEqual(q->AppendSearchDomains, 0);
-    XCTAssertNil((__bridge id)q->DuplicateOf);
-    
-    // Call mDNS_Execute to see if the new question, q, has an answer in the cache.
-    // It won't be yet because the cache is empty.
-    m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
-    mDNS_Execute(m);
-    
-    // Verify mDNS_Execute processed the new question.
-    XCTAssertNil((__bridge id)m->NewQuestions);
-    
-    // Verify the cache is empty and the request got no reply.
-    XCTAssertEqual(m->rrcache_totalused, 0);
-    XCTAssertNil((__bridge id)req->replies);
-}
-#if 0
-// This unit test receives a canned uDNS response message by calling the mDNSCoreReceive() function.
-// It then verifies cache entries were added for the CNAME and A records that were contained in the
-// answers of the canned response, query_response_msgbuf.  This unit test also verifies that
-// 2 add events were generated for the client.
-- (void)testPopulateCacheWithClientResponseRecords
-{
-    mDNS *const m = &mDNSStorage;
-    DNSMessage *msgptr = (DNSMessage *)query_response_msgbuf;
-    size_t msgsz = sizeof(query_response_msgbuf);
-    struct reply_state *reply;
-    request_state* req = client_request_message;
-    DNSQuestion *q = &req->u.queryrecord.q;
-    const char *data;
-    const char *end;
-    char name[kDNSServiceMaxDomainName];
-    uint16_t rrtype, rrclass, rdlen;
-    const char *rdata;
-    size_t len;
-    char domainname_cstr[MAX_ESCAPED_DOMAIN_NAME];
-    
-    // Receive and populate the cache with canned response
-    receive_response(req, msgptr, msgsz);
-    
-    // Verify 2 cache entries for CName and A record are present
-    mDNSu32 CacheUsed =0, notUsed =0;
-    LogCacheRecords_ut(mDNS_TimeNow(m), &CacheUsed, &notUsed);
-    XCTAssertEqual(CacheUsed, m->rrcache_totalused);
-    XCTAssertEqual(CacheUsed, 4); // 2 for the CacheGroup object plus 2 for the A and CNAME records
-    XCTAssertEqual(m->PktNum, 1); // one packet was received
-    
-    // Verify question's qname is now set with the A record's domainname
-    ConvertDomainNameToCString(&q->qname, domainname_cstr);
-    XCTAssertEqual(q->qnamehash, DomainNameHashValue(&q->qname));
-    XCTAssertFalse(strcmp(domainname_cstr, udns_cname_domainname_cstr));
-    
-    // Verify client's add event for CNAME is properly formed
-    reply = req->replies;
-    XCTAssertNotEqual(reply, (reply_state*)mDNSNULL);
-    XCTAssertNil((__bridge id)reply->next);
-    
-    data    = (char *)&reply->rhdr[1];
-    end     = data+reply->totallen;
-    get_string(&data, data+reply->totallen, name, kDNSServiceMaxDomainName);
-    rrtype  = get_uint16(&data, end);
-    rrclass = get_uint16(&data, end);
-    rdlen   = get_uint16(&data, end);
-    rdata   = get_rdata(&data, end, rdlen);
-    len     = get_reply_len(name, rdlen);
-    
-    XCTAssertEqual(reply->totallen, len + sizeof(ipc_msg_hdr));
-    XCTAssertEqual(reply->mhdr->version, VERSION);
-    XCTAssertEqual(reply->mhdr->datalen, len);
-    XCTAssertEqual(reply->mhdr->ipc_flags, 0);
-    XCTAssertEqual(reply->mhdr->op, query_reply_op);
-    
-    XCTAssertEqual(reply->rhdr->flags, htonl(kDNSServiceFlagsAdd));
-    XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexAny);
-    XCTAssertEqual(reply->rhdr->error, kDNSServiceErr_NoError);
-    
-    XCTAssertEqual(rrtype, kDNSType_CNAME);
-    XCTAssertEqual(rrclass, kDNSClass_IN);
-    ConvertDomainNameToCString((const domainname *const)rdata, domainname_cstr);
-    XCTAssertFalse(strcmp(domainname_cstr, "test212.dotbennu.com."));
-    
-    // The mDNS_Execute call generates an add event for the A record
-    m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
-    mDNS_Execute(m);
-    
-    // Verify the client's reply contains a properly formed add event for the A record.
-    reply = req->replies;
-    XCTAssertNotEqual(reply, (reply_state*)mDNSNULL);
-    XCTAssertNotEqual(reply->next, (reply_state*)mDNSNULL);
-    reply = reply->next;
-    
-    data    = (char *)&reply->rhdr[1];
-    end     = data+reply->totallen;
-    get_string(&data, data+reply->totallen, name, kDNSServiceMaxDomainName);
-    rrtype  = get_uint16(&data, end);
-    rrclass = get_uint16(&data, end);
-    rdlen   = get_uint16(&data, end);
-    rdata   = get_rdata(&data, end, rdlen);
-    len     = get_reply_len(name, rdlen);
-    
-    XCTAssertEqual(reply->totallen, len + sizeof(ipc_msg_hdr));
-    XCTAssertEqual(reply->mhdr->version, VERSION);
-    XCTAssertEqual(reply->mhdr->datalen, len);
-    
-    XCTAssertEqual(reply->mhdr->ipc_flags, 0);
-    XCTAssertEqual(reply->mhdr->op, query_reply_op);
-    
-    XCTAssertEqual(reply->rhdr->flags, htonl(kDNSServiceFlagsAdd));
-    XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexAny);
-    XCTAssertEqual(reply->rhdr->error, kDNSServiceErr_NoError);
-    
-    XCTAssertEqual(rrtype, kDNSType_A);
-    XCTAssertEqual(rrclass, kDNSClass_IN);
-    XCTAssertEqual(rdata[0], dns_response_ipv4.b[0]);
-    XCTAssertEqual(rdata[1], dns_response_ipv4.b[1]);
-    XCTAssertEqual(rdata[2], dns_response_ipv4.b[2]);
-    XCTAssertEqual(rdata[3], dns_response_ipv4.b[3]);
-}
-
-// This function verifies the cache and event handling occurred as expected when a network change happened.
-// The uDNS_SetupDNSConfig is called to simulate a network change and two outcomes occur. First the A record
-// query is restarted and sent to a new DNS server. Second the cache records are purged. Then mDNS_Execute
-// is called and it removes the purged cache records and generates a remove event for the A record.
-// The following are verified:
-//      1.) The restart of query for A record.
-//      2.) The cache is empty after mDNS_Execute removes the cache entres.
-//      3.) The remove event is verified by examining the request's reply data.
-- (void)testSimulateNetworkChangeAndVerify
-{
-    mDNS *const m = &mDNSStorage;
-    request_state*  req = client_request_message;
-    DNSQuestion*    q = &req->u.queryrecord.q;
-    mDNSu32 CacheUsed =0, notUsed =0;
-    const char *data;    const char *end;
-    char name[kDNSServiceMaxDomainName];
-    uint16_t rrtype, rrclass, rdlen;
-    const char *rdata;
-    size_t len;
-    
-    // The uDNS_SetupDNSConfig reconfigures the resolvers so the A record query is restarted and
-    // both the CNAME and A record are purged.
-    uDNS_SetupDNSConfig(m);
-    
-    // Verify the A record query was restarted.  This is done indirectly by noticing the transaction id and interval have changed.
-    XCTAssertEqual(q->ThisQInterval, InitialQuestionInterval);
-    XCTAssertNotEqual(q->TargetQID.NotAnInteger, uDNS_TargetQID);
-    
-    // Then mDNS_Execute removes both records from the cache and calls the client back with a remove event for A record.
-    m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
-    mDNS_Execute(m);
-    
-    // Verify the cache entries are removed
-    LogCacheRecords_ut(mDNS_TimeNow(m), &CacheUsed, &notUsed);
-    XCTAssertEqual(CacheUsed, m->rrcache_totalused);
-    XCTAssertEqual(CacheUsed, 0);
-    
-    // Verify the A record's remove event is setup as expected in the reply data
-    struct reply_state *reply;
-    reply = req->replies;
-    XCTAssertNotEqual(reply, (reply_state*)mDNSNULL);
-    XCTAssertNotEqual(reply->next, (reply_state*)mDNSNULL);
-    XCTAssertNotEqual(reply->next->next, (reply_state*)mDNSNULL);
-
-    reply = reply->next->next; // Get to last event to verify remove event
-    data    = (char *)&reply->rhdr[1];
-    end     = data+reply->totallen;
-    get_string(&data, data+reply->totallen, name, kDNSServiceMaxDomainName);
-    rrtype  = get_uint16(&data, end);
-    rrclass = get_uint16(&data, end);
-    rdlen   = get_uint16(&data, end);
-    rdata   = get_rdata(&data, end, rdlen);
-    len     = get_reply_len(name, rdlen);
-    
-    XCTAssertEqual(reply->totallen, reply->mhdr->datalen + sizeof(ipc_msg_hdr));
-    XCTAssertEqual(reply->mhdr->version, VERSION);
-    XCTAssertEqual(reply->mhdr->datalen, len);
-    XCTAssertEqual(reply->mhdr->ipc_flags, 0);
-    XCTAssertEqual(reply->mhdr->op, query_reply_op);
-    
-    XCTAssertNotEqual(reply->rhdr->flags, htonl(kDNSServiceFlagsAdd));
-    XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexAny);
-    XCTAssertEqual(reply->rhdr->error, kDNSServiceErr_NoError);
-    
-    XCTAssertEqual(rrtype, kDNSType_A);
-    XCTAssertEqual(rrclass, kDNSClass_IN);
-    XCTAssertEqual(rdata[0], dns_response_ipv4.b[0]);
-    XCTAssertEqual(rdata[1], dns_response_ipv4.b[1]);
-    XCTAssertEqual(rdata[2], dns_response_ipv4.b[2]);
-    XCTAssertEqual(rdata[3], dns_response_ipv4.b[3]);
-}
-#endif
-
-@end
diff --git a/mDNSMacOSX/Tests/DNSMessageTest.m b/mDNSMacOSX/Tests/DNSMessageTest.m
deleted file mode 100644 (file)
index d30e010..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (c) 2017-2018 Apple Inc. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "mDNSEmbeddedAPI.h"
-#include "DNSCommon.h"
-#import <XCTest/XCTest.h>
-
-@interface DNSMessageTest : XCTestCase
-{
-    DNSMessage *msg;
-}
-@end
-
-@implementation DNSMessageTest
-
-- (void)setUp
-{
-    msg = (DNSMessage *)malloc (sizeof(DNSMessage));
-    XCTAssert(msg != NULL);
-    
-    // message header should be 12 bytes
-    XCTAssertEqual(sizeof(msg->h),        12);
-}
-
-- (void)tearDown
-{
-    XCTAssert(msg != NULL);
-    free(msg);
-}
-
-- (void)testMessageInitialization
-{
-    // Initialize the message
-    InitializeDNSMessage(&msg->h, onesID, QueryFlags);
-    
-    // Check that the message is initialized properly
-    XCTAssertEqual(msg->h.numAdditionals, 0);
-    XCTAssertEqual(msg->h.numAnswers,     0);
-    XCTAssertEqual(msg->h.numQuestions,   0);
-    XCTAssertEqual(msg->h.numAuthorities, 0);
-}
-
-#if 0
-- (void)testPerformanceExample {
-    // This is an example of a performance test case.
-    [self measureBlock:^{
-        // Put the code you want to measure the time of here.
-    }];
-}
-#endif
-
-@end
diff --git a/mDNSMacOSX/Tests/HelperFunctionTest.m b/mDNSMacOSX/Tests/HelperFunctionTest.m
deleted file mode 100644 (file)
index 4497a9d..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-//
-//  HelperFunctionTest.m
-//  Tests
-//
-//  Copyright (c) 2019 Apple Inc. All rights reserved.
-//
-
-#import <XCTest/XCTest.h>
-#include "unittest_common.h"
-
-@interface HelperFunctionTest : XCTestCase
-
-@end
-
-@implementation HelperFunctionTest
-
-- (void)setUp {
-    // It is empty for now.
-}
-
-- (void)tearDown {
-    // It is empty for now.
-}
-
-- (void)testCFStringToDomainLabel {
-    // test_cstring[i][0] is the input
-    // test_cstring[i][1] is the expected correct output
-    static const char * const test_cstring[][2] = {
-        {"short", "short"},
-        {"this-is-a-normal-computer-name", "this-is-a-normal-computer-name"},
-        {"", ""},
-        {"This is an ascii string whose length is more than 63 bytes, where it takes one byte to store every character", "This is an ascii string whose length is more than 63 bytes, whe"},
-        {"यह एक एस्सी स्ट्रिंग है जिसकी लंबाई साठ तीन बाइट्स से अधिक है, जहां यह हर चरित्र को संग्रहीत करने के लिए एक बाइट लेता है", "यह एक एस्सी स्ट्रिंग है "}, // "यह एक एस्सी स्ट्रिंग है " is 62 byte, and "यह एक एस्सी स्ट्रिंग है जि" is more than 63, so the result is expected to truncated to 62 bytes instead of 63 bytes
-        {"वितीय टेस्ट ट्राई टी॰वी॰", "वितीय टेस्ट ट्राई टी॰वी"},
-        {"这是一个超过六十三比特的其中每个中文字符占三比特的中文字符串", "这是一个超过六十三比特的其中每个中文字符占"},
-        {"🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝", "🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝"} // "🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝" is 60 bytes, and "🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝" is more than 63 bytes so the result is expected to be truncated to 60 bytes instead of 64 bytes
-    };
-
-    for (int i = 0, n = sizeof(test_cstring) / sizeof(test_cstring[0]); i < n; i++) {
-        // construct CFString from input
-        CFStringRef name_ref = CFStringCreateWithCString(kCFAllocatorDefault, test_cstring[i][0], kCFStringEncodingUTF8);
-        XCTAssertTrue(name_ref != NULL, @"unit test internal error. {descrption=\"name_ref should be non-NULL.\"}");
-
-        // call the function being tested
-        domainlabel label;
-        mDNSDomainLabelFromCFString_ut(name_ref, &label);
-
-        // Check if the result is correct
-        XCTAssertEqual(label.c[0], strlen(test_cstring[i][1]),
-                       @"name length is not equal. {expect=%d,actual=%d}", strlen(test_cstring[i][1]), label.c[0]);
-        XCTAssertTrue(memcmp(label.c + 1, test_cstring[i][1], label.c[0]) == 0,
-                      @"name is not correctly decoded. {expect='%s',actual='%s'}", test_cstring[i][1], label.c + 1);
-
-        CFRelease(name_ref);
-    }
-}
-
-@end
diff --git a/mDNSMacOSX/Tests/Info.plist b/mDNSMacOSX/Tests/Info.plist
deleted file mode 100644 (file)
index 6c40a6c..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-       <key>CFBundleDevelopmentRegion</key>
-       <string>$(DEVELOPMENT_LANGUAGE)</string>
-       <key>CFBundleExecutable</key>
-       <string>$(EXECUTABLE_NAME)</string>
-       <key>CFBundleIdentifier</key>
-       <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
-       <key>CFBundleInfoDictionaryVersion</key>
-       <string>6.0</string>
-       <key>CFBundleName</key>
-       <string>$(PRODUCT_NAME)</string>
-       <key>CFBundlePackageType</key>
-       <string>BNDL</string>
-       <key>CFBundleShortVersionString</key>
-       <string>1.0</string>
-       <key>CFBundleVersion</key>
-       <string>1</string>
-</dict>
-</plist>
diff --git a/mDNSMacOSX/Tests/LocalOnlyTimeoutTest.m b/mDNSMacOSX/Tests/LocalOnlyTimeoutTest.m
deleted file mode 100644 (file)
index 9e0e952..0000000
+++ /dev/null
@@ -1,373 +0,0 @@
-/*
- * Copyright (c) 2017-2019 Apple Inc. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "unittest_common.h"
-#include "mDNSMacOSX.h"
-#import <XCTest/XCTest.h>
-
-// This query request message was generated from the following command: "dns-sd -lo -timeout -Q cardinal2.apple.com. A"
-char query_req_msgbuf[33]= {
-       0x00, 0x01, 0x90, 0x00,
-       // DNSServiceFlags.L = (kDNSServiceFlagsReturnIntermediates |kDNSServiceFlagsSuppressUnusable | kDNSServiceFlagsTimeout)
-       0xff, 0xff, 0xff, 0xff,
-       // interfaceIndex = mDNSInterface_LocalOnly
-       0x63, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c,
-       0x32, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x00, 0x00, 0x01, 0x00,
-       0x01
-};
-
-mDNSlocal mStatus InitEtcHostsRecords(void)
-{
-    mDNS *m = &mDNSStorage;
-    struct sockaddr_storage hostaddr;
-    
-    AuthHash newhosts;
-    mDNSPlatformMemZero(&newhosts, sizeof(AuthHash));
-    
-    memset(&hostaddr, 0, sizeof(hostaddr));
-    get_ip("127.0.0.1", &hostaddr);
-    
-    domainname domain;
-    MakeDomainNameFromDNSNameString(&domain, "localhost");
-    
-    mDNSMacOSXCreateEtcHostsEntry_ut(&domain, (struct sockaddr *) &hostaddr, mDNSNULL, mDNSNULL, &newhosts);
-    
-    memset(&hostaddr, 0, sizeof(hostaddr));
-    get_ip("0000:0000:0000:0000:0000:0000:0000:0001", &hostaddr);
-    
-    MakeDomainNameFromDNSNameString(&domain, "localhost");
-    
-    mDNSMacOSXCreateEtcHostsEntry_ut(&domain, (struct sockaddr *) &hostaddr, mDNSNULL, mDNSNULL, &newhosts);
-    
-    memset(&hostaddr, 0, sizeof(hostaddr));
-    get_ip("255.255.255.255", &hostaddr);
-    
-    MakeDomainNameFromDNSNameString(&domain, "broadcasthost");
-    
-    mDNSMacOSXCreateEtcHostsEntry_ut(&domain, (struct sockaddr *) &hostaddr, mDNSNULL, mDNSNULL, &newhosts);
-    
-    memset(&hostaddr, 0, sizeof(hostaddr));
-    get_ip("17.226.40.200", &hostaddr);
-    
-    MakeDomainNameFromDNSNameString(&domain, "cardinal2.apple.com");
-    
-    mDNSMacOSXCreateEtcHostsEntry_ut(&domain, (struct sockaddr *) &hostaddr, mDNSNULL, mDNSNULL, &newhosts);
-    UpdateEtcHosts_ut(&newhosts);
-    
-    m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
-    mDNS_Execute(m);
-    
-    return mStatus_NoError;
-}
-
-@interface LocalOnlyATimeoutTest : XCTestCase
-{
-    request_state* client_request_message;
-    UDPSocket* local_socket;
-    char domainname_cstr[MAX_ESCAPED_DOMAIN_NAME];
-}
-@end
-
-@implementation LocalOnlyATimeoutTest
-
-// The InitUnitTest() initializes a minimal mDNSResponder environment as
-// well as allocates memory for a local_socket and client request.
-// It also sets the domainname_cstr specified in the client's query request.
-// Note: This unit test does not send packets on the wire and it does not open sockets.
-- (void)setUp
-{
-       // Init mDNSStorage
-       mStatus result = init_mdns_storage();
-    XCTAssertEqual(result, mStatus_NoError);
-
-       // Allocate a client request
-       local_socket = (UDPSocket *)calloc(1, sizeof(*local_socket));
-
-       // Allocate memory for a request that is used to make client requests.
-       client_request_message = calloc(1, sizeof(request_state));
-
-       // Init domainname that is used by unit tests
-       strlcpy(domainname_cstr, "cardinal2.apple.com.", sizeof(domainname_cstr));
-}
-
-// This function does memory cleanup and no verification.
-- (void)tearDown
-{
-    mDNSPlatformMemFree(local_socket);
-}
-
-// This unit test starts a local only request for "cardinal2.apple.com.".  It first
-// calls start_client_request to start a query, it then verifies the
-// req and query data structures are set as expected. Next, the cache is verified to
-// be empty by AnswerNewLocalOnlyQuestion() and so results in GenerateNegativeResponse()
-// getting called which sets up a reply with a negative answer in it for the client.
-// On return from mDNS_Execute, the client's reply structure is verified to be set as
-// expected. Lastly the timeout is simulated and mDNS_Execute is called. This results
-// in a call to TimeoutQuestions(). And again, the GenerateNegativeResponse() is called
-// which returns a negative response to the client.  This time the client reply is verified
-// to be setup with a timeout result.
-- (void)testStartLocalOnlyClientQueryRequest
-{
-       mDNS *const m = &mDNSStorage;
-    request_state* req = client_request_message;
-       char *msgptr = (char *)query_req_msgbuf;
-       size_t msgsz = sizeof(query_req_msgbuf);
-       DNSQuestion *q;
-       mDNSs32 min_size = sizeof(DNSServiceFlags) + sizeof(mDNSu32) + 4;
-       mStatus err = mStatus_NoError;
-       char qname_cstr[MAX_ESCAPED_DOMAIN_NAME];
-       struct reply_state *reply;
-       size_t len;
-
-       // Process the unit test's client request
-       start_client_request(req, msgptr, msgsz, query_request, local_socket);
-       XCTAssertEqual(err, mStatus_NoError);
-
-       // Verify the query initialized and request fields were set as expected
-       XCTAssertEqual(req->hdr.version, VERSION);
-       XCTAssertGreaterThan((mDNSs32)req->data_bytes, min_size);
-       XCTAssertEqual(req->flags, (kDNSServiceFlagsSuppressUnusable | kDNSServiceFlagsReturnIntermediates | kDNSServiceFlagsTimeout));
-       XCTAssertEqual(req->interfaceIndex, kDNSServiceInterfaceIndexLocalOnly);
-    XCTAssertNotEqual(req->terminate, (req_termination_fn)0);
-
-       q = &req->u.queryrecord.op.q;
-       XCTAssertEqual(q, m->NewLocalOnlyQuestions);
-       XCTAssertNil((__bridge id)m->Questions);
-       XCTAssertNil((__bridge id)m->NewQuestions);
-       XCTAssertEqual(q->SuppressUnusable, 1);
-       XCTAssertEqual(q->ReturnIntermed, 1);
-       XCTAssertEqual(q->Suppressed, mDNSfalse);                                                                       // Regress <rdar://problem/27571734>
-
-       ConvertDomainNameToCString(&q->qname, qname_cstr);
-       XCTAssertFalse(strcmp(qname_cstr, domainname_cstr));
-       XCTAssertEqual(q->qnamehash, DomainNameHashValue(&q->qname));
-
-       XCTAssertEqual(q->InterfaceID, mDNSInterface_LocalOnly);
-       XCTAssertEqual(q->flags, req->flags);
-       XCTAssertEqual(q->qtype, 1);
-       XCTAssertEqual(q->qclass, 1);
-       XCTAssertEqual(q->LongLived, 0);
-       XCTAssertEqual(q->ExpectUnique, mDNSfalse);
-       XCTAssertEqual(q->ForceMCast, 0);
-       XCTAssertEqual(q->TimeoutQuestion, 1);
-       XCTAssertEqual(q->WakeOnResolve, 0);
-       XCTAssertEqual(q->UseBackgroundTraffic, 0);
-       XCTAssertEqual(q->ValidationRequired, 0);
-       XCTAssertEqual(q->ValidatingResponse, 0);
-       XCTAssertEqual(q->ProxyQuestion, 0);
-    XCTAssertNotEqual((void*)q->QuestionCallback, (void*)mDNSNULL);
-       XCTAssertNil((__bridge id)q->DNSSECAuthInfo);
-    XCTAssertNil((__bridge id)(void*)q->DAIFreeCallback);
-       XCTAssertNotEqual(q->StopTime, 0);
-       XCTAssertEqual(q->AppendSearchDomains, 0);
-    XCTAssertNil((__bridge id)q->DuplicateOf);
-
-       // At this point the the cache is empty. Calling mDNS_Execute will answer the local-only
-       // question with a negative response.
-       m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
-       mDNS_Execute(m);  // Regress <rdar://problem/28721294>
-
-       // Verify reply is a negative response and error code is set to kDNSServiceErr_NoSuchRecord error.
-       reply = req->replies;
-    XCTAssertNotEqual(reply, (reply_state*)mDNSNULL);
-
-       XCTAssertNil((__bridge id)m->NewLocalOnlyQuestions);
-       XCTAssertEqual(q->LOAddressAnswers, 0);
-
-       len = get_reply_len(qname_cstr, 0);
-
-       XCTAssertNil((__bridge id)reply->next);
-       XCTAssertEqual(reply->totallen, reply->mhdr->datalen + sizeof(ipc_msg_hdr));
-       XCTAssertEqual(reply->mhdr->version, VERSION);
-       XCTAssertEqual(reply->mhdr->datalen, len);
-       XCTAssertEqual(reply->mhdr->ipc_flags, 0);
-       XCTAssertEqual(reply->mhdr->op, query_reply_op);
-
-    XCTAssertTrue((reply->rhdr->flags & htonl(kDNSServiceFlagsAdd)));
-       XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexLocalOnly);       // Regress <rdar://problem/27340874>
-       XCTAssertEqual(reply->rhdr->error,
-                                       (DNSServiceErrorType)htonl(kDNSServiceErr_NoSuchRecord));       // Regress <rdar://problem/24827555>
-
-       // Simulate what udsserver_idle normally does for clean up
-       freeL("StartLocalOnlyClientQueryRequest:reply", reply);
-       req->replies = NULL;
-
-       // Simulate the query time out of the local-only question.
-       // The expected behavior is a negative answer with time out error
-       m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
-       q->StopTime = mDNS_TimeNow_NoLock(m);
-       m->NextScheduledStopTime -= mDNSPlatformOneSecond*5;
-       mDNS_Execute(m);
-
-       // Verify the reply is a negative response with timeout error.
-       reply = req->replies;
-    XCTAssertNotEqual(reply, (reply_state*)mDNSNULL);
-    XCTAssertNil((__bridge id)m->NewLocalOnlyQuestions);
-    XCTAssertEqual(q->LOAddressAnswers, 0);
-
-       len = get_reply_len(qname_cstr, 0);
-
-    XCTAssertNil((__bridge id)reply->next);
-       XCTAssertEqual(reply->totallen, len + sizeof(ipc_msg_hdr));
-       XCTAssertEqual(reply->mhdr->version, VERSION);
-       XCTAssertEqual(reply->mhdr->datalen, len);
-       XCTAssertEqual(reply->mhdr->ipc_flags, 0);
-       XCTAssertEqual(reply->mhdr->op, query_reply_op);
-    XCTAssertTrue((reply->rhdr->flags & htonl(kDNSServiceFlagsAdd)));
-       XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexLocalOnly);       // Regress <rdar://problem/27340874>
-       XCTAssertEqual(reply->rhdr->error,
-                                       (DNSServiceErrorType)htonl(kDNSServiceErr_Timeout));            // Regress <rdar://problem/27562965>
-
-       // Free request and reallocate to use when query is restarted
-       free_req(req);
-       client_request_message = calloc(1, sizeof(request_state));
-}
-
-// This unit test populates the cache with four /etc/hosts records and then
-// verifies there are four entries in the cache.
-- (void)testPopulateCacheWithClientLOResponseRecords
-{
-       mDNS *const m = &mDNSStorage;
-
-       // Verify cache is empty
-       int count = LogEtcHosts_ut(m);
-       XCTAssertEqual(count, 0);
-
-       // Populate /etc/hosts
-       mStatus result = InitEtcHostsRecords();
-       XCTAssertEqual(result, mStatus_NoError);
-
-       // mDNS_Execute is called to populate the /etc/hosts cache.
-       m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
-       mDNS_Execute(m);
-
-       count = LogEtcHosts_ut(m);
-       XCTAssertEqual(count, 4);
-    
-    [self _testRestartLocalOnlyClientQueryRequest];   //  Continuation of this test
-}
-
-// This unit test starts a local only request for "cardinal2.apple.com.".  It first
-// calls start_client_request to start a query, it then verifies the
-// req and query data structures are set as expected. Next, the cache is verified to
-// contain the answer by AnswerNewLocalOnlyQuestion() and so results in setting up an
-// answer reply to the client. On return from mDNS_Execute, the client's reply structure
-// is verified to be set as expected. Lastly the timeout is simulated and mDNS_Execute is
-// called. This results in a call to TimeoutQuestions(). And this time, the
-// GenerateNegativeResponse() is called which returns a negative response to the client
-// which specifies the timeout occurred. Again, the answer reply is verified to
-// to specify a timeout.
-- (void)_testRestartLocalOnlyClientQueryRequest
-{
-       mDNS *const m = &mDNSStorage;
-       request_state* req = client_request_message;
-       char *msgptr = (char *)query_req_msgbuf;
-       size_t msgsz = sizeof(query_req_msgbuf);        DNSQuestion *q;
-       mDNSs32 min_size = sizeof(DNSServiceFlags) + sizeof(mDNSu32) + 4;
-       mStatus err = mStatus_NoError;
-       char qname_cstr[MAX_ESCAPED_DOMAIN_NAME];
-       struct reply_state *reply;
-       size_t len;
-
-       // Process the unit test's client request
-       start_client_request(req, msgptr, msgsz, query_request, local_socket);
-    XCTAssertEqual(err, mStatus_NoError);
-
-       XCTAssertEqual(req->hdr.version, VERSION);
-    XCTAssertGreaterThan((mDNSs32)req->data_bytes, min_size);
-       XCTAssertEqual(req->flags, (kDNSServiceFlagsSuppressUnusable | kDNSServiceFlagsReturnIntermediates | kDNSServiceFlagsTimeout));
-       XCTAssertEqual(req->interfaceIndex, kDNSServiceInterfaceIndexLocalOnly);
-    XCTAssertNotEqual(req->terminate, (req_termination_fn)0);
-    XCTAssertNil((__bridge id)m->Questions);
-
-       q = &req->u.queryrecord.op.q;
-       XCTAssertEqual(q, m->NewLocalOnlyQuestions);
-       XCTAssertEqual(q->SuppressUnusable, 1);
-       XCTAssertEqual(q->ReturnIntermed, 1);
-       XCTAssertEqual(q->Suppressed, mDNSfalse);                                                                               // Regress <rdar://problem/27571734>
-       XCTAssertEqual(q->qnamehash, DomainNameHashValue(&q->qname));
-       XCTAssertEqual(q->InterfaceID, mDNSInterface_LocalOnly);
-       XCTAssertEqual(q->flags, req->flags);
-       XCTAssertEqual(q->qtype, 1);
-       XCTAssertEqual(q->qclass, 1);
-       XCTAssertEqual(q->LongLived, 0);
-       XCTAssertEqual(q->ExpectUnique, mDNSfalse);
-       XCTAssertEqual(q->ForceMCast, 0);
-       XCTAssertEqual(q->TimeoutQuestion, 1);
-       XCTAssertEqual(q->WakeOnResolve, 0);
-       XCTAssertEqual(q->UseBackgroundTraffic, 0);
-       XCTAssertEqual(q->ValidationRequired, 0);
-       XCTAssertEqual(q->ValidatingResponse, 0);
-       XCTAssertEqual(q->ProxyQuestion, 0);
-    XCTAssertNotEqual((void*)q->QuestionCallback, (void*)mDNSNULL);
-    XCTAssertNil((__bridge id)q->DNSSECAuthInfo);
-    XCTAssertNil((__bridge id)(void*)q->DAIFreeCallback);
-    XCTAssertNotEqual(q->StopTime, 0);
-    XCTAssertEqual(q->AppendSearchDomains, 0);
-    XCTAssertNil((__bridge id)q->DuplicateOf);
-       ConvertDomainNameToCString(&q->qname, qname_cstr);
-    XCTAssertFalse(strcmp(qname_cstr, domainname_cstr));
-
-       // Answer local-only question with found cache entry
-       m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
-       mDNS_Execute(m);                                                                                                                        // Regress <rdar://problem/28721294>
-    XCTAssertNil((__bridge id)m->NewLocalOnlyQuestions);
-       XCTAssertEqual(req->u.queryrecord.op.answered, 1);
-       XCTAssertEqual(q->LOAddressAnswers, 1);
-       XCTAssertEqual(q, m->LocalOnlyQuestions);
-
-       reply = req->replies;
-       len = get_reply_len(qname_cstr, 4);
-
-    XCTAssertNil((__bridge id)reply->next);
-       XCTAssertEqual(reply->totallen, len + sizeof(ipc_msg_hdr));
-       XCTAssertEqual(reply->mhdr->version, VERSION);
-       XCTAssertEqual(reply->mhdr->datalen, len);
-       XCTAssertEqual(reply->mhdr->ipc_flags, 0);
-       XCTAssertEqual(reply->mhdr->op, query_reply_op);
-       XCTAssertTrue((reply->rhdr->flags & htonl(kDNSServiceFlagsAdd)));
-       XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexLocalOnly);       // Regress <rdar://problem/27340874>
-       XCTAssertEqual(reply->rhdr->error, kDNSServiceErr_NoError);
-
-       // Simulate the query time out of the local-only question.
-       // The expected behavior is a negative answer with time out error
-       m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
-       q->StopTime = mDNS_TimeNow_NoLock(m);
-       m->NextScheduledStopTime -= mDNSPlatformOneSecond*5;
-       mDNS_Execute(m);
-
-       reply = req->replies->next;
-       XCTAssertNotEqual(reply, (reply_state*)mDNSNULL);
-    XCTAssertNil((__bridge id)reply->next);
-    XCTAssertNil((__bridge id)m->NewLocalOnlyQuestions);
-       XCTAssertEqual(q->LOAddressAnswers, 0);
-       len = get_reply_len(qname_cstr, 0);
-
-    XCTAssertNil((__bridge id)reply->next);
-       XCTAssertEqual(reply->totallen, len + + sizeof(ipc_msg_hdr));
-       XCTAssertEqual(reply->mhdr->version, VERSION);
-       XCTAssertEqual(reply->mhdr->datalen, len);
-       XCTAssertEqual(reply->mhdr->ipc_flags, 0);
-       XCTAssertEqual(reply->mhdr->op, query_reply_op);
-    XCTAssertTrue((reply->rhdr->flags & htonl(kDNSServiceFlagsAdd)));
-       XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexLocalOnly);       // Regress <rdar://problem/27340874>
-       XCTAssertEqual(reply->rhdr->error,
-                                       (DNSServiceErrorType)htonl(kDNSServiceErr_Timeout));            // Regress <rdar://problem/27562965>
-
-       free_req(req);
-}
-
-@end
diff --git a/mDNSMacOSX/Tests/ResourceRecordTest.m b/mDNSMacOSX/Tests/ResourceRecordTest.m
deleted file mode 100644 (file)
index 8a71f43..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (c) 2017-2018 Apple Inc. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "mDNSEmbeddedAPI.h"
-#include "DNSCommon.h"
-#import <XCTest/XCTest.h>
-
-@interface ResourceRecordTest : XCTestCase
-{
-}
-@end
-
-@implementation ResourceRecordTest
-
-- (void)setUp
-{
-}
-
-- (void)tearDown
-{
-}
-
-- (void)testTXTSetup
-{
-    AuthRecord authRec;
-    mDNS_SetupResourceRecord(&authRec, mDNSNULL, mDNSInterface_Any, kDNSType_TXT, kStandardTTL, kDNSRecordTypeShared, AuthRecordAny,mDNSNULL, mDNSNULL);
-    XCTAssertEqual(authRec.resrec.rrtype,               kDNSType_TXT);
-    XCTAssertEqual(authRec.resrec.RecordType,           kDNSRecordTypeShared);
-    XCTAssertEqual(authRec.resrec.rdata->MaxRDLength,   sizeof(RDataBody));
-}
-
-- (void)testASetup
-{
-    AuthRecord authRec;
-    mDNS_SetupResourceRecord(&authRec, mDNSNULL, mDNSInterface_Any, kDNSType_A, kHostNameTTL, kDNSRecordTypeUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
-    
-    XCTAssertEqual(authRec.resrec.rrtype,           kDNSType_A);
-    XCTAssertEqual(authRec.resrec.RecordType,       kDNSRecordTypeUnique);
-    // Add more verifications
-}
-
-- (void)testOPTSetup
-{
-    AuthRecord opt;
-    mDNSu32    updatelease = 7200;
-
-    // Setup the OPT Record
-    mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
-
-    // Verify the basic initialization is all ok
-
-    opt.resrec.rrclass    = NormalMaxDNSMessageData;
-    opt.resrec.rdlength   = sizeof(rdataOPT);   // One option in this OPT record
-    opt.resrec.rdestimate = sizeof(rdataOPT);
-    opt.resrec.rdata->u.opt[0].opt           = kDNSOpt_Lease;
-    opt.resrec.rdata->u.opt[0].u.updatelease = updatelease;
-
-    // Put the resource record in and verify everything is fine
-#if 0
-    mDNSu8     data[AbsoluteMaxDNSMessageData];
-    mDNSu8     *p = data;
-    mDNSu16    numAdditionals;
-    
-    p = PutResourceRecordTTLWithLimit((DNSMessage*)&data, p, &numAdditionals, &opt.resrec, opt.resrec.rroriginalttl, data + AbsoluteMaxDNSMessageData);
-#endif
-}
-
-// Repeat with bad data to make sure it bails out cleanly
-
-#if 0
-- (void)testPerformanceExample {
-    // This is an example of a performance test case.
-    [self measureBlock:^{
-        // Put the code you want to measure the time of here.
-    }];
-}
-#endif
-
-@end
diff --git a/mDNSMacOSX/Tests/Unit Tests/CNameRecordTest.m b/mDNSMacOSX/Tests/Unit Tests/CNameRecordTest.m
new file mode 100644 (file)
index 0000000..f96d4ad
--- /dev/null
@@ -0,0 +1,374 @@
+/*
+ * Copyright (c) 2017-2019 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "unittest_common.h"
+#import <XCTest/XCTest.h>
+
+struct UDPSocket_struct
+{
+       mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port
+};
+typedef struct UDPSocket_struct UDPSocket;
+
+// This client request was generated using the following command: "dns-sd -Q 123server.dotbennu.com. A".
+uint8_t query_client_msgbuf[35] = {
+       0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x32, 0x33, 0x73, 0x65, 0x72, 0x76, 0x65,
+       0x72, 0x2e, 0x64, 0x6f, 0x74, 0x62, 0x65, 0x6e, 0x6e, 0x75, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00,
+       0x01, 0x00, 0x01
+};
+
+// This uDNS message is a canned response that was originally captured by wireshark.
+uint8_t query_response_msgbuf[108] = {
+    0x69, 0x41, // transaction id
+       0x85, 0x80, // flags
+       0x00, 0x01, // 1 question for 123server.dotbennu.com. Addr
+       0x00, 0x02,     // 2 anwsers: 123server.dotbennu.com. CNAME test212.dotbennu.com., test212.dotbennu.com. Addr 10.100.0.1,
+       0x00, 0x01,     // 1 authorities anwser: dotbennu.com. NS cardinal2.apple.com.
+       0x00, 0x00, 0x09, 0x31, 0x32, 0x33,
+    0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x08, 0x64, 0x6f, 0x74, 0x62, 0x65, 0x6e, 0x6e, 0x75, 0x03,
+    0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00,
+    0x02, 0x56, 0x00, 0x0a, 0x07, 0x74, 0x65, 0x73, 0x74, 0x32, 0x31, 0x32, 0xc0, 0x16, 0xc0, 0x34,
+    0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x04, 0x0a, 0x64, 0x00, 0x01, 0xc0, 0x16,
+    0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0x51, 0x80, 0x00, 0x12, 0x09, 0x63, 0x61, 0x72, 0x64, 0x69,
+    0x6e, 0x61, 0x6c, 0x32, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x65, 0xc0, 0x1f
+};
+
+// Variables associated with contents of the above uDNS message
+#define uDNS_TargetQID 16745
+char udns_original_domainname_cstr[] = "123server.dotbennu.com.";
+char udns_cname_domainname_cstr[] = "test212.dotbennu.com.";
+static const mDNSv4Addr dns_response_ipv4 = {{ 10, 100, 0, 1 }};
+
+@interface CNameRecordTest : XCTestCase
+{
+    UDPSocket* local_socket;
+    request_state* client_request_message;}
+@end
+
+@implementation CNameRecordTest
+
+// The InitThisUnitTest() initializes the mDNSResponder environment as well as
+// a DNSServer. It also allocates memory for a local_socket and client request.
+// Note: This unit test does not send packets on the wire and it does not open sockets.
+- (void)setUp
+{
+    // Init unit test environment and verify no error occurred.
+    mStatus result = init_mdns_environment(mDNStrue);
+    XCTAssertEqual(result, mStatus_NoError);
+    
+    // Add one DNS server and verify it was added.
+    AddDNSServer_ut();
+    XCTAssertEqual(CountOfUnicastDNSServers(&mDNSStorage), 1);
+    
+    // Create memory for a socket that is never used or opened.
+    local_socket = (UDPSocket *) mDNSPlatformMemAllocateClear(sizeof(*local_socket));
+    
+    // Create memory for a request that is used to make this unit test's client request.
+    client_request_message = calloc(1, sizeof(request_state));
+}
+
+- (void)tearDown
+{
+    mDNS *m = &mDNSStorage;
+    request_state* req = client_request_message;
+    DNSServer   *ptr, **p = &m->DNSServers;
+    
+    while (req->replies)
+    {
+        reply_state *reply = req->replies;
+        req->replies = req->replies->next;
+        mDNSPlatformMemFree(reply);
+    }
+    mDNSPlatformMemFree(req);
+    
+    mDNSPlatformMemFree(local_socket);
+    
+    while (*p)
+    {
+        ptr = *p;
+        *p = (*p)->next;
+        LogInfo("FinalizeUnitTest: Deleting server %p %#a:%d (%##s)", ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c);
+        mDNSPlatformMemFree(ptr);
+    }
+}
+
+- (void)testCNameRecordTestSeries
+{
+    [self _startClientQueryRequest];
+    [self _populateCacheWithClientResponseRecords];
+    [self _simulateNetworkChangeAndVerify];
+}
+
+// This test simulates a uds client request by setting up a client request and then
+// calling mDNSResponder's handle_client_request.  The handle_client_request function
+// processes the request and starts a query.  This unit test verifies
+// the client request and query were setup as expected.  This unit test also calls
+// mDNS_execute which determines the cache does not contain the new question's
+// answer.
+- (void)_startClientQueryRequest
+{
+    mDNS *const m = &mDNSStorage;
+    request_state* req = client_request_message;
+    char *msgptr = (char *)query_client_msgbuf;
+    size_t msgsz = sizeof(query_client_msgbuf);
+    mDNSs32 min_size = sizeof(DNSServiceFlags) + sizeof(mDNSu32) + 4;
+    DNSQuestion *q;
+    mStatus err = mStatus_NoError;
+    char qname_cstr[MAX_ESCAPED_DOMAIN_NAME];
+    
+    // Process the unit test's client request
+    start_client_request(req, msgptr, msgsz, query_request, local_socket);
+    XCTAssertEqual(err, mStatus_NoError);
+    
+    // Verify the request fields were set as expected
+    XCTAssertNil((__bridge id)req->next);
+    XCTAssertNil((__bridge id)req->primary);
+    XCTAssertEqual(req->sd, client_req_sd);
+    XCTAssertEqual(req->process_id, client_req_process_id);
+    XCTAssertFalse(strcmp(req->pid_name, client_req_pid_name));
+    XCTAssertEqual(req->validUUID, mDNSfalse);
+    XCTAssertEqual(req->errsd, 0);
+    XCTAssertEqual(req->uid, client_req_uid);
+    XCTAssertEqual(req->ts, t_complete);
+    XCTAssertGreaterThan((mDNSs32)req->data_bytes, min_size);
+    XCTAssertEqual(req->msgend, msgptr+msgsz);
+    XCTAssertNil((__bridge id)(void*)req->msgbuf);
+    XCTAssertEqual(req->hdr.version, VERSION);
+    XCTAssertNil((__bridge id)req->replies);
+    XCTAssertNotEqual(req->terminate, (req_termination_fn)0);
+    XCTAssertEqual(req->flags, kDNSServiceFlagsReturnIntermediates);
+    XCTAssertEqual(req->interfaceIndex, kDNSServiceInterfaceIndexAny);
+    
+    // Verify the query fields were set as expected
+    q = &req->u.queryrecord.op.q;
+    XCTAssertNotEqual(q, (DNSQuestion *)mDNSNULL);
+    XCTAssertEqual(q, m->Questions);
+    XCTAssertEqual(q, m->NewQuestions);
+    XCTAssertEqual(q->SuppressUnusable, mDNSfalse);
+    XCTAssertEqual(q->ReturnIntermed, mDNStrue);
+    XCTAssertEqual(q->Suppressed, mDNSfalse);
+    
+    ConvertDomainNameToCString(&q->qname, qname_cstr);
+    XCTAssertFalse(strcmp(qname_cstr, udns_original_domainname_cstr));
+    XCTAssertEqual(q->qnamehash, DomainNameHashValue(&q->qname));
+    
+    XCTAssertEqual(q->InterfaceID, mDNSInterface_Any);
+    XCTAssertEqual(q->flags, req->flags);
+    XCTAssertEqual(q->qtype, 1);
+    XCTAssertEqual(q->qclass, 1);
+    XCTAssertEqual(q->LongLived, 0);
+    XCTAssertEqual(q->ExpectUnique, mDNSfalse);
+    XCTAssertEqual(q->ForceMCast, 0);
+    XCTAssertEqual(q->TimeoutQuestion, 0);
+    XCTAssertEqual(q->WakeOnResolve, 0);
+    XCTAssertEqual(q->UseBackgroundTraffic, 0);
+    XCTAssertEqual(q->ValidationRequired, 0);
+    XCTAssertEqual(q->ValidatingResponse, 0);
+    XCTAssertEqual(q->ProxyQuestion, 0);
+    XCTAssertNotEqual((void*)q->QuestionCallback, (void*)mDNSNULL);
+    XCTAssertNil((__bridge id)q->DNSSECAuthInfo);
+    XCTAssertNil((__bridge id)(void*)q->DAIFreeCallback);
+    XCTAssertEqual(q->AppendSearchDomains, 0);
+    XCTAssertNil((__bridge id)q->DuplicateOf);
+    
+    // Call mDNS_Execute to see if the new question, q, has an answer in the cache.
+    // It won't be yet because the cache is empty.
+    m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
+    mDNS_Execute(m);
+    
+    // Verify mDNS_Execute processed the new question.
+    XCTAssertNil((__bridge id)m->NewQuestions);
+    
+    // Verify the cache is empty and the request got no reply.
+    XCTAssertEqual(m->rrcache_totalused, 0);
+    XCTAssertNil((__bridge id)req->replies);
+}
+
+// This unit test receives a canned uDNS response message by calling the mDNSCoreReceive() function.
+// It then verifies cache entries were added for the CNAME and A records that were contained in the
+// answers of the canned response, query_response_msgbuf.  This unit test also verifies that
+// 2 add events were generated for the client.
+- (void)_populateCacheWithClientResponseRecords
+{
+    mDNS *const m = &mDNSStorage;
+    DNSMessage *msgptr = (DNSMessage *)query_response_msgbuf;
+    size_t msgsz = sizeof(query_response_msgbuf);
+    struct reply_state *reply;
+    request_state* req = client_request_message;
+    DNSQuestion *q = &req->u.queryrecord.op.q;
+    const char *data;
+    const char *end;
+    char name[kDNSServiceMaxDomainName];
+    uint16_t rrtype, rrclass, rdlen;
+    const char *rdata;
+    size_t len;
+    char domainname_cstr[MAX_ESCAPED_DOMAIN_NAME];
+    
+    // Receive and populate the cache with canned response
+    receive_response(req, msgptr, msgsz);
+    
+    // Verify 2 cache entries for CName and A record are present
+    mDNSu32 CacheUsed =0, notUsed =0;
+    LogCacheRecords_ut(mDNS_TimeNow(m), &CacheUsed, &notUsed);
+    XCTAssertEqual(CacheUsed, m->rrcache_totalused);
+    XCTAssertEqual(CacheUsed, 4); // 2 for the CacheGroup object plus 2 for the A and CNAME records
+    XCTAssertEqual(m->PktNum, 1); // one packet was received
+    
+    // Verify question's qname is now set with the A record's domainname
+    ConvertDomainNameToCString(&q->qname, domainname_cstr);
+    XCTAssertEqual(q->qnamehash, DomainNameHashValue(&q->qname));
+    XCTAssertFalse(strcmp(domainname_cstr, udns_cname_domainname_cstr));
+    
+    // Verify client's add event for CNAME is properly formed
+    reply = req->replies;
+    XCTAssertNotEqual(reply, (reply_state*)mDNSNULL);
+    XCTAssertNil((__bridge id)reply->next);
+    
+    data    = (char *)&reply->rhdr[1];
+    end     = data+reply->totallen;
+    get_string(&data, data+reply->totallen, name, kDNSServiceMaxDomainName);
+    rrtype  = get_uint16(&data, end);
+    rrclass = get_uint16(&data, end);
+    rdlen   = get_uint16(&data, end);
+    rdata   = get_rdata(&data, end, rdlen);
+    len     = get_reply_len(name, rdlen);
+    
+    XCTAssertEqual(reply->totallen, len + sizeof(ipc_msg_hdr));
+    XCTAssertEqual(reply->mhdr->version, VERSION);
+    XCTAssertEqual(reply->mhdr->datalen, len);
+    XCTAssertEqual(reply->mhdr->ipc_flags, 0);
+    XCTAssertEqual(reply->mhdr->op, query_reply_op);
+    
+    XCTAssertEqual(reply->rhdr->flags, htonl(kDNSServiceFlagsAdd));
+    XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexAny);
+    XCTAssertEqual(reply->rhdr->error, kDNSServiceErr_NoError);
+    
+    XCTAssertEqual(rrtype, kDNSType_CNAME);
+    XCTAssertEqual(rrclass, kDNSClass_IN);
+    ConvertDomainNameToCString((const domainname *const)rdata, domainname_cstr);
+    XCTAssertFalse(strcmp(domainname_cstr, "test212.dotbennu.com."));
+    
+    // The mDNS_Execute call generates an add event for the A record
+    m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
+    mDNS_Execute(m);
+    
+    // Verify the client's reply contains a properly formed add event for the A record.
+    reply = req->replies;
+    XCTAssertNotEqual(reply, (reply_state*)mDNSNULL);
+    XCTAssertNotEqual(reply->next, (reply_state*)mDNSNULL);
+    reply = reply->next;
+    
+    data    = (char *)&reply->rhdr[1];
+    end     = data+reply->totallen;
+    get_string(&data, data+reply->totallen, name, kDNSServiceMaxDomainName);
+    rrtype  = get_uint16(&data, end);
+    rrclass = get_uint16(&data, end);
+    rdlen   = get_uint16(&data, end);
+    rdata   = get_rdata(&data, end, rdlen);
+    len     = get_reply_len(name, rdlen);
+    
+    XCTAssertEqual(reply->totallen, len + sizeof(ipc_msg_hdr));
+    XCTAssertEqual(reply->mhdr->version, VERSION);
+    XCTAssertEqual(reply->mhdr->datalen, len);
+    
+    XCTAssertEqual(reply->mhdr->ipc_flags, 0);
+    XCTAssertEqual(reply->mhdr->op, query_reply_op);
+    
+    XCTAssertEqual(reply->rhdr->flags, htonl(kDNSServiceFlagsAdd));
+    XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexAny);
+    XCTAssertEqual(reply->rhdr->error, kDNSServiceErr_NoError);
+    
+    XCTAssertEqual(rrtype, kDNSType_A);
+    XCTAssertEqual(rrclass, kDNSClass_IN);
+    XCTAssertEqual(rdata[0], dns_response_ipv4.b[0]);
+    XCTAssertEqual(rdata[1], dns_response_ipv4.b[1]);
+    XCTAssertEqual(rdata[2], dns_response_ipv4.b[2]);
+    XCTAssertEqual(rdata[3], dns_response_ipv4.b[3]);
+}
+
+// This function verifies the cache and event handling occurred as expected when a network change happened.
+// The uDNS_SetupDNSConfig is called to simulate a network change and two outcomes occur. First the A record
+// query is restarted and sent to a new DNS server. Second the cache records are purged. Then mDNS_Execute
+// is called and it removes the purged cache records and generates a remove event for the A record.
+// The following are verified:
+//      1.) The restart of query for A record.
+//      2.) The cache is empty after mDNS_Execute removes the cache entres.
+//      3.) The remove event is verified by examining the request's reply data.
+- (void)_simulateNetworkChangeAndVerify
+{
+    mDNS *const m = &mDNSStorage;
+    request_state*  req = client_request_message;
+    DNSQuestion*    q = &req->u.queryrecord.op.q;
+    mDNSu32 CacheUsed =0, notUsed =0;
+    const char *data;    const char *end;
+    char name[kDNSServiceMaxDomainName];
+    uint16_t rrtype, rrclass, rdlen;
+    const char *rdata;
+    size_t len;
+    
+    // The uDNS_SetupDNSConfig reconfigures the resolvers so the A record query is restarted and
+    // both the CNAME and A record are purged.
+    force_uDNS_SetupDNSConfig_ut(m);
+
+    // Verify the A record query was restarted.  This is done indirectly by noticing the transaction id and interval have changed.
+    XCTAssertEqual(q->ThisQInterval, InitialQuestionInterval);
+    XCTAssertNotEqual(q->TargetQID.NotAnInteger, uDNS_TargetQID);
+    
+    // Then mDNS_Execute removes both records from the cache and calls the client back with a remove event for A record.
+    m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
+    mDNS_Execute(m);
+    
+    // Verify the cache entries are removed
+    LogCacheRecords_ut(mDNS_TimeNow(m), &CacheUsed, &notUsed);
+    XCTAssertEqual(CacheUsed, m->rrcache_totalused);
+    XCTAssertEqual(CacheUsed, 0);
+    
+    // Verify the A record's remove event is setup as expected in the reply data
+    struct reply_state *reply;
+    reply = req->replies;
+    XCTAssertNotEqual(reply, (reply_state*)mDNSNULL);
+    XCTAssertNotEqual(reply->next, (reply_state*)mDNSNULL);
+    XCTAssertNotEqual(reply->next->next, (reply_state*)mDNSNULL);
+
+    reply = reply->next->next; // Get to last event to verify remove event
+    data    = (char *)&reply->rhdr[1];
+    end     = data+reply->totallen;
+    get_string(&data, data+reply->totallen, name, kDNSServiceMaxDomainName);
+    rrtype  = get_uint16(&data, end);
+    rrclass = get_uint16(&data, end);
+    rdlen   = get_uint16(&data, end);
+    rdata   = get_rdata(&data, end, rdlen);
+    len     = get_reply_len(name, rdlen);
+    
+    XCTAssertEqual(reply->totallen, reply->mhdr->datalen + sizeof(ipc_msg_hdr));
+    XCTAssertEqual(reply->mhdr->version, VERSION);
+    XCTAssertEqual(reply->mhdr->datalen, len);
+    XCTAssertEqual(reply->mhdr->ipc_flags, 0);
+    XCTAssertEqual(reply->mhdr->op, query_reply_op);
+    
+    XCTAssertNotEqual(reply->rhdr->flags, htonl(kDNSServiceFlagsAdd));
+    XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexAny);
+    XCTAssertEqual(reply->rhdr->error, kDNSServiceErr_NoError);
+    
+    XCTAssertEqual(rrtype, kDNSType_A);
+    XCTAssertEqual(rrclass, kDNSClass_IN);
+    XCTAssertEqual(rdata[0], dns_response_ipv4.b[0]);
+    XCTAssertEqual(rdata[1], dns_response_ipv4.b[1]);
+    XCTAssertEqual(rdata[2], dns_response_ipv4.b[2]);
+    XCTAssertEqual(rdata[3], dns_response_ipv4.b[3]);
+}
+
+@end
diff --git a/mDNSMacOSX/Tests/Unit Tests/CacheOrderTest.m b/mDNSMacOSX/Tests/Unit Tests/CacheOrderTest.m
new file mode 100644 (file)
index 0000000..facd79b
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2017-2019 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "unittest_common.h"
+#import <XCTest/XCTest.h>
+
+struct UDPSocket_struct
+{
+       mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port
+};
+typedef struct UDPSocket_struct UDPSocket;
+
+// This client request was generated using the following command: "dns-sd -Q web.mydomain.test".
+uint8_t test_order_query_msgbuf[30] = {
+    0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x65, 0x62, 0x2e, 0x6d, 0x79, 0x64,
+    0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x01, 0x00, 0x01
+};
+#if 0
+0000   10 c1 01 00 00 01 00 00 00 00 00 00 03 77 65 62
+0010   08 6d 79 64 6f 6d 61 69 6e 04 74 65 73 74 00 00
+0020   01 00 01
+
+0000   ef 53 01 00 00 01 00 00 00 00 00 00 03 77 65 62
+0010   08 6d 79 64 6f 6d 61 69 6e 04 74 65 73 74 00 00
+0020   01 00 01
+
+uint8_t test_query_client_msgbuf[35] = {
+    0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x32, 0x33, 0x73, 0x65, 0x72, 0x76, 0x65,
+    0x72, 0x2e, 0x64, 0x6f, 0x74, 0x62, 0x65, 0x6e, 0x6e, 0x75, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00,
+    0x01, 0x00, 0x01
+};
+#endif
+// This uDNS message is a canned response that was originally captured by wireshark.
+uint8_t test_order_response1_msgbuf[228] = {
+    0x0f, 0x98, // transaction id
+    0x85, 0x80, // flags
+    0x00, 0x01, // 1 query: web.mydomain.test: type A, class IN
+    0x00, 0x04, // 4 anwsers: Addr 10.0.0.101, Addr 10.0.0.105, Addr 10.0.0.104, Addr 10.0.0.102
+    0x00, 0x01, // 1 authoritative nameservers: mydomain.test: type NS, class IN, ns ns.mydomain.test
+    0x00, 0x01, // 1 additional: ns.mydomain.test: type A, class IN, addr 192.168.0.23
+    0x03, 0x77, 0x65, 0x62,
+    0x08, 0x6d, 0x79, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x00,
+    0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x0a,
+    0x00, 0x00, 0x65, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x0a,
+    0x00, 0x00, 0x69, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x0a,
+    0x00, 0x00, 0x68, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x0a,
+    0x00, 0x00, 0x66, 0xc0, 0x10, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0x51, 0x80, 0x00, 0x05, 0x02,
+    0x6e, 0x73, 0xc0, 0x10, 0xc0, 0x6f, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x51, 0x80, 0x00, 0x04,
+    0xc0, 0xa8, 0x00, 0x17
+};
+
+// This uDNS message is a canned response that was originally captured by wireshark, then modified to match above (other than Addr order).
+uint8_t test_order_response2_msgbuf[228] = {
+    0x0f, 0x98, // transaction id
+    0x85, 0x80, // flags
+    0x00, 0x01, // 1 query: web.mydomain.test: type A, class IN
+    0x00, 0x04, // 4 anwsers: Addr 10.0.0.102, Addr 10.0.0.101, Addr 10.0.0.104, Addr 10.0.0.105
+    0x00, 0x01, // 1 authoritative nameservers: mydomain.test: type NS, class IN, ns ns.mydomain.test
+    0x00, 0x01, // 1 additional: ns.mydomain.test: type A, class IN, addr 192.168.0.23
+    0x03, 0x77, 0x65, 0x62,
+    0x08, 0x6d, 0x79, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x04, 0x74, 0x65, 0x73, 0x74, 0x00, 0x00,
+    0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x0a,
+    0x00, 0x00, 0x66, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x0a,
+    0x00, 0x00, 0x65, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x0a,
+    0x00, 0x00, 0x68, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x0a,
+    0x00, 0x00, 0x69, 0xc0, 0x10, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0x51, 0x80, 0x00, 0x05, 0x02,
+    0x6e, 0x73, 0xc0, 0x10, 0xc0, 0x6f, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x51, 0x80, 0x00, 0x04,
+    0xc0, 0xa8, 0x00, 0x17
+};
+
+// Variables associated with contents of the above uDNS message
+char test_order_domainname_cstr[] = "web.mydomain.test.";
+
+@interface CacheOrderTest : XCTestCase
+{
+    UDPSocket* local_socket;
+    request_state* client_request_message;}
+@end
+
+@implementation CacheOrderTest
+
+// The InitThisUnitTest() initializes the mDNSResponder environment as well as
+// a DNSServer. It also allocates memory for a local_socket and client request.
+// Note: This unit test does not send packets on the wire and it does not open sockets.
+- (void)setUp
+{
+    mDNSPlatformMemZero(&mDNSStorage, sizeof(mDNS));
+
+    // Init unit test environment and verify no error occurred.
+    mStatus result = init_mdns_environment(mDNStrue);
+    XCTAssertEqual(result, mStatus_NoError);
+    
+    // Add one DNS server and verify it was added.
+    AddDNSServer_ut();
+    XCTAssertEqual(CountOfUnicastDNSServers(&mDNSStorage), 1);
+    
+    // Create memory for a socket that is never used or opened.
+    local_socket = (UDPSocket *) mDNSPlatformMemAllocateClear(sizeof(*local_socket));
+    
+    // Create memory for a request that is used to make this unit test's client request.
+    client_request_message = calloc(1, sizeof(request_state));
+}
+
+- (void)tearDown
+{
+    mDNS *m = &mDNSStorage;
+    request_state* req = client_request_message;
+    DNSServer   *ptr, **p = &m->DNSServers;
+    
+    while (req->replies)
+    {
+        reply_state *reply = req->replies;
+        req->replies = req->replies->next;
+        mDNSPlatformMemFree(reply);
+    }
+    mDNSPlatformMemFree(req);
+    
+    mDNSPlatformMemFree(local_socket);
+    
+    while (*p)
+    {
+        ptr = *p;
+        *p = (*p)->next;
+        LogInfo("FinalizeUnitTest: Deleting server %p %#a:%d (%##s)", ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c);
+        mDNSPlatformMemFree(ptr);
+    }
+}
+
+- (void)testSuspiciousReplyTestSeries
+{
+    [self _clientQueryRequest];
+    [self _verifyCacheOrderBehavior];
+}
+
+// Simulate a uds client request by setting up a client request and then
+// calling mDNSResponder's handle_client_request.  The handle_client_request function
+// processes the request and starts a query.  This unit test verifies
+// the client request and query were setup as expected.  This unit test also calls
+// mDNS_execute which determines the cache does not contain the new question's
+// answer.
+- (void)_clientQueryRequest
+{
+    mDNS *const m = &mDNSStorage;
+    request_state* req = client_request_message;
+    char *msgptr = (char *)test_order_query_msgbuf;
+    size_t msgsz = sizeof(test_order_query_msgbuf);
+    mDNSs32 min_size = sizeof(DNSServiceFlags) + sizeof(mDNSu32) + 4;
+    DNSQuestion *q;
+    mStatus err = mStatus_NoError;
+    char qname_cstr[MAX_ESCAPED_DOMAIN_NAME];
+    
+    // Process the unit test's client request
+    start_client_request(req, msgptr, msgsz, query_request, local_socket);
+    XCTAssertEqual(err, mStatus_NoError);
+    
+    // Verify the request fields were set as expected
+    XCTAssertNil((__bridge id)req->next);
+    XCTAssertNil((__bridge id)req->primary);
+    XCTAssertEqual(req->sd, client_req_sd);
+    XCTAssertEqual(req->process_id, client_req_process_id);
+    XCTAssertFalse(strcmp(req->pid_name, client_req_pid_name));
+    XCTAssertEqual(req->validUUID, mDNSfalse);
+    XCTAssertEqual(req->errsd, 0);
+    XCTAssertEqual(req->uid, client_req_uid);
+    XCTAssertEqual(req->ts, t_complete);
+    XCTAssertGreaterThan((mDNSs32)req->data_bytes, min_size);
+    XCTAssertEqual(req->msgend, msgptr+msgsz);
+    XCTAssertNil((__bridge id)(void*)req->msgbuf);
+    XCTAssertEqual(req->hdr.version, VERSION);
+    XCTAssertNil((__bridge id)req->replies);
+    XCTAssertNotEqual(req->terminate, (req_termination_fn)0);
+    XCTAssertEqual(req->flags, kDNSServiceFlagsReturnIntermediates);
+    XCTAssertEqual(req->interfaceIndex, kDNSServiceInterfaceIndexAny);
+    
+    // Verify the query fields were set as expected
+    q = &req->u.queryrecord.op.q;
+    XCTAssertNotEqual(q, (DNSQuestion *)mDNSNULL);
+    XCTAssertEqual(q, m->Questions);
+    XCTAssertEqual(q, m->NewQuestions);
+    XCTAssertEqual(q->SuppressUnusable, mDNSfalse);
+    XCTAssertEqual(q->ReturnIntermed, mDNStrue);
+    XCTAssertEqual(q->Suppressed, mDNSfalse);
+    
+    ConvertDomainNameToCString(&q->qname, qname_cstr);
+    XCTAssertFalse(strcmp(qname_cstr, test_order_domainname_cstr));
+    XCTAssertEqual(q->qnamehash, DomainNameHashValue(&q->qname));
+    
+    XCTAssertEqual(q->InterfaceID, mDNSInterface_Any);
+    XCTAssertEqual(q->flags, req->flags);
+    XCTAssertEqual(q->qtype, 1);
+    XCTAssertEqual(q->qclass, 1);
+    XCTAssertEqual(q->LongLived, 0);
+    XCTAssertEqual(q->ExpectUnique, mDNSfalse);
+    XCTAssertEqual(q->ForceMCast, 0);
+    XCTAssertEqual(q->TimeoutQuestion, 0);
+    XCTAssertEqual(q->WakeOnResolve, 0);
+    XCTAssertEqual(q->UseBackgroundTraffic, 0);
+    XCTAssertEqual(q->ValidationRequired, 0);
+    XCTAssertEqual(q->ValidatingResponse, 0);
+    XCTAssertEqual(q->ProxyQuestion, 0);
+    XCTAssertNotEqual((void*)q->QuestionCallback, (void*)mDNSNULL);
+    XCTAssertNil((__bridge id)q->DNSSECAuthInfo);
+    XCTAssertNil((__bridge id)(void*)q->DAIFreeCallback);
+    XCTAssertEqual(q->AppendSearchDomains, 0);
+    XCTAssertNil((__bridge id)q->DuplicateOf);
+    
+    // Call mDNS_Execute to see if the new question, q, has an answer in the cache.
+    // It won't be yet because the cache is empty.
+    m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
+    mDNS_Execute(m);
+    
+    // Verify mDNS_Execute processed the new question.
+    XCTAssertNil((__bridge id)m->NewQuestions);
+    
+    // Verify the cache is empty and the request got no reply.
+    XCTAssertEqual(m->rrcache_totalused, 0);
+    XCTAssertNil((__bridge id)req->replies);
+}
+
+// This unit test performs two queries and verifies the cache oredr is updated on a new response.
+// 1) Verify response is ordered in the cache as expected
+// 2) Test again with new response, and verify cache order is updated
+- (void)_verifyCacheOrderBehavior
+{
+    mDNS *const m = &mDNSStorage;
+    DNSMessage *msgptr;
+    size_t msgsz;
+    request_state* req = client_request_message;
+    DNSQuestion *q = &req->u.queryrecord.op.q;
+    mStatus status;
+
+    // 1)
+    // Process first response
+    // Verify response cache count & order
+
+    msgptr = (DNSMessage *)test_order_response1_msgbuf;
+    msgsz = sizeof(test_order_response1_msgbuf);
+    receive_response(req, msgptr, msgsz);
+    
+    // Verify records received
+    mDNSu32 CacheUsed =0, notUsed =0;
+    LogCacheRecords_ut(mDNS_TimeNow(m), &CacheUsed, &notUsed);
+    XCTAssertEqual(CacheUsed, 5);               // Verify 4 records received + Cache Group
+
+    // Verify record order
+    mDNSu8  lastoctet1[4] = {101, 105, 104, 102};
+    status = verify_cache_addr_order_for_domain_ut(m, lastoctet1, 4, &q->qname);
+    XCTAssertEqual(status, mStatus_NoError, @"Cache order test 1 failed");
+
+    // 2)
+    // Process second response
+    // Verify response cache count & order
+
+    msgptr = (DNSMessage *)test_order_response2_msgbuf;
+    msgsz = sizeof(test_order_response2_msgbuf);
+    receive_response(req, msgptr, msgsz);
+
+    // Verify records received
+    LogCacheRecords_ut(mDNS_TimeNow(m), &CacheUsed, &notUsed);
+    XCTAssertEqual(CacheUsed, 5);               // Verify 4 records received + Cache Group
+
+    // Verify record order
+    mDNSu8  lastoctet2[4] = {102, 101, 104, 105};
+    status = verify_cache_addr_order_for_domain_ut(m, lastoctet2, 4, &q->qname);
+    XCTAssertEqual(status, mStatus_NoError, @"Cache order test 2 failed");
+}
+
+
+@end
diff --git a/mDNSMacOSX/Tests/Unit Tests/DNSMessageTest.m b/mDNSMacOSX/Tests/Unit Tests/DNSMessageTest.m
new file mode 100644 (file)
index 0000000..d30e010
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2017-2018 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mDNSEmbeddedAPI.h"
+#include "DNSCommon.h"
+#import <XCTest/XCTest.h>
+
+@interface DNSMessageTest : XCTestCase
+{
+    DNSMessage *msg;
+}
+@end
+
+@implementation DNSMessageTest
+
+- (void)setUp
+{
+    msg = (DNSMessage *)malloc (sizeof(DNSMessage));
+    XCTAssert(msg != NULL);
+    
+    // message header should be 12 bytes
+    XCTAssertEqual(sizeof(msg->h),        12);
+}
+
+- (void)tearDown
+{
+    XCTAssert(msg != NULL);
+    free(msg);
+}
+
+- (void)testMessageInitialization
+{
+    // Initialize the message
+    InitializeDNSMessage(&msg->h, onesID, QueryFlags);
+    
+    // Check that the message is initialized properly
+    XCTAssertEqual(msg->h.numAdditionals, 0);
+    XCTAssertEqual(msg->h.numAnswers,     0);
+    XCTAssertEqual(msg->h.numQuestions,   0);
+    XCTAssertEqual(msg->h.numAuthorities, 0);
+}
+
+#if 0
+- (void)testPerformanceExample {
+    // This is an example of a performance test case.
+    [self measureBlock:^{
+        // Put the code you want to measure the time of here.
+    }];
+}
+#endif
+
+@end
diff --git a/mDNSMacOSX/Tests/Unit Tests/HelperFunctionTest.m b/mDNSMacOSX/Tests/Unit Tests/HelperFunctionTest.m
new file mode 100644 (file)
index 0000000..4497a9d
--- /dev/null
@@ -0,0 +1,58 @@
+//
+//  HelperFunctionTest.m
+//  Tests
+//
+//  Copyright (c) 2019 Apple Inc. All rights reserved.
+//
+
+#import <XCTest/XCTest.h>
+#include "unittest_common.h"
+
+@interface HelperFunctionTest : XCTestCase
+
+@end
+
+@implementation HelperFunctionTest
+
+- (void)setUp {
+    // It is empty for now.
+}
+
+- (void)tearDown {
+    // It is empty for now.
+}
+
+- (void)testCFStringToDomainLabel {
+    // test_cstring[i][0] is the input
+    // test_cstring[i][1] is the expected correct output
+    static const char * const test_cstring[][2] = {
+        {"short", "short"},
+        {"this-is-a-normal-computer-name", "this-is-a-normal-computer-name"},
+        {"", ""},
+        {"This is an ascii string whose length is more than 63 bytes, where it takes one byte to store every character", "This is an ascii string whose length is more than 63 bytes, whe"},
+        {"यह एक एस्सी स्ट्रिंग है जिसकी लंबाई साठ तीन बाइट्स से अधिक है, जहां यह हर चरित्र को संग्रहीत करने के लिए एक बाइट लेता है", "यह एक एस्सी स्ट्रिंग है "}, // "यह एक एस्सी स्ट्रिंग है " is 62 byte, and "यह एक एस्सी स्ट्रिंग है जि" is more than 63, so the result is expected to truncated to 62 bytes instead of 63 bytes
+        {"वितीय टेस्ट ट्राई टी॰वी॰", "वितीय टेस्ट ट्राई टी॰वी"},
+        {"这是一个超过六十三比特的其中每个中文字符占三比特的中文字符串", "这是一个超过六十三比特的其中每个中文字符占"},
+        {"🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝", "🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝"} // "🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝" is 60 bytes, and "🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝🃝" is more than 63 bytes so the result is expected to be truncated to 60 bytes instead of 64 bytes
+    };
+
+    for (int i = 0, n = sizeof(test_cstring) / sizeof(test_cstring[0]); i < n; i++) {
+        // construct CFString from input
+        CFStringRef name_ref = CFStringCreateWithCString(kCFAllocatorDefault, test_cstring[i][0], kCFStringEncodingUTF8);
+        XCTAssertTrue(name_ref != NULL, @"unit test internal error. {descrption=\"name_ref should be non-NULL.\"}");
+
+        // call the function being tested
+        domainlabel label;
+        mDNSDomainLabelFromCFString_ut(name_ref, &label);
+
+        // Check if the result is correct
+        XCTAssertEqual(label.c[0], strlen(test_cstring[i][1]),
+                       @"name length is not equal. {expect=%d,actual=%d}", strlen(test_cstring[i][1]), label.c[0]);
+        XCTAssertTrue(memcmp(label.c + 1, test_cstring[i][1], label.c[0]) == 0,
+                      @"name is not correctly decoded. {expect='%s',actual='%s'}", test_cstring[i][1], label.c + 1);
+
+        CFRelease(name_ref);
+    }
+}
+
+@end
diff --git a/mDNSMacOSX/Tests/Unit Tests/Info.plist b/mDNSMacOSX/Tests/Unit Tests/Info.plist
new file mode 100644 (file)
index 0000000..6c40a6c
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>$(DEVELOPMENT_LANGUAGE)</string>
+       <key>CFBundleExecutable</key>
+       <string>$(EXECUTABLE_NAME)</string>
+       <key>CFBundleIdentifier</key>
+       <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundleName</key>
+       <string>$(PRODUCT_NAME)</string>
+       <key>CFBundlePackageType</key>
+       <string>BNDL</string>
+       <key>CFBundleShortVersionString</key>
+       <string>1.0</string>
+       <key>CFBundleVersion</key>
+       <string>1</string>
+</dict>
+</plist>
diff --git a/mDNSMacOSX/Tests/Unit Tests/LocalOnlyTimeoutTest.m b/mDNSMacOSX/Tests/Unit Tests/LocalOnlyTimeoutTest.m
new file mode 100644 (file)
index 0000000..9e0e952
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ * Copyright (c) 2017-2019 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "unittest_common.h"
+#include "mDNSMacOSX.h"
+#import <XCTest/XCTest.h>
+
+// This query request message was generated from the following command: "dns-sd -lo -timeout -Q cardinal2.apple.com. A"
+char query_req_msgbuf[33]= {
+       0x00, 0x01, 0x90, 0x00,
+       // DNSServiceFlags.L = (kDNSServiceFlagsReturnIntermediates |kDNSServiceFlagsSuppressUnusable | kDNSServiceFlagsTimeout)
+       0xff, 0xff, 0xff, 0xff,
+       // interfaceIndex = mDNSInterface_LocalOnly
+       0x63, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c,
+       0x32, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x00, 0x00, 0x01, 0x00,
+       0x01
+};
+
+mDNSlocal mStatus InitEtcHostsRecords(void)
+{
+    mDNS *m = &mDNSStorage;
+    struct sockaddr_storage hostaddr;
+    
+    AuthHash newhosts;
+    mDNSPlatformMemZero(&newhosts, sizeof(AuthHash));
+    
+    memset(&hostaddr, 0, sizeof(hostaddr));
+    get_ip("127.0.0.1", &hostaddr);
+    
+    domainname domain;
+    MakeDomainNameFromDNSNameString(&domain, "localhost");
+    
+    mDNSMacOSXCreateEtcHostsEntry_ut(&domain, (struct sockaddr *) &hostaddr, mDNSNULL, mDNSNULL, &newhosts);
+    
+    memset(&hostaddr, 0, sizeof(hostaddr));
+    get_ip("0000:0000:0000:0000:0000:0000:0000:0001", &hostaddr);
+    
+    MakeDomainNameFromDNSNameString(&domain, "localhost");
+    
+    mDNSMacOSXCreateEtcHostsEntry_ut(&domain, (struct sockaddr *) &hostaddr, mDNSNULL, mDNSNULL, &newhosts);
+    
+    memset(&hostaddr, 0, sizeof(hostaddr));
+    get_ip("255.255.255.255", &hostaddr);
+    
+    MakeDomainNameFromDNSNameString(&domain, "broadcasthost");
+    
+    mDNSMacOSXCreateEtcHostsEntry_ut(&domain, (struct sockaddr *) &hostaddr, mDNSNULL, mDNSNULL, &newhosts);
+    
+    memset(&hostaddr, 0, sizeof(hostaddr));
+    get_ip("17.226.40.200", &hostaddr);
+    
+    MakeDomainNameFromDNSNameString(&domain, "cardinal2.apple.com");
+    
+    mDNSMacOSXCreateEtcHostsEntry_ut(&domain, (struct sockaddr *) &hostaddr, mDNSNULL, mDNSNULL, &newhosts);
+    UpdateEtcHosts_ut(&newhosts);
+    
+    m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
+    mDNS_Execute(m);
+    
+    return mStatus_NoError;
+}
+
+@interface LocalOnlyATimeoutTest : XCTestCase
+{
+    request_state* client_request_message;
+    UDPSocket* local_socket;
+    char domainname_cstr[MAX_ESCAPED_DOMAIN_NAME];
+}
+@end
+
+@implementation LocalOnlyATimeoutTest
+
+// The InitUnitTest() initializes a minimal mDNSResponder environment as
+// well as allocates memory for a local_socket and client request.
+// It also sets the domainname_cstr specified in the client's query request.
+// Note: This unit test does not send packets on the wire and it does not open sockets.
+- (void)setUp
+{
+       // Init mDNSStorage
+       mStatus result = init_mdns_storage();
+    XCTAssertEqual(result, mStatus_NoError);
+
+       // Allocate a client request
+       local_socket = (UDPSocket *)calloc(1, sizeof(*local_socket));
+
+       // Allocate memory for a request that is used to make client requests.
+       client_request_message = calloc(1, sizeof(request_state));
+
+       // Init domainname that is used by unit tests
+       strlcpy(domainname_cstr, "cardinal2.apple.com.", sizeof(domainname_cstr));
+}
+
+// This function does memory cleanup and no verification.
+- (void)tearDown
+{
+    mDNSPlatformMemFree(local_socket);
+}
+
+// This unit test starts a local only request for "cardinal2.apple.com.".  It first
+// calls start_client_request to start a query, it then verifies the
+// req and query data structures are set as expected. Next, the cache is verified to
+// be empty by AnswerNewLocalOnlyQuestion() and so results in GenerateNegativeResponse()
+// getting called which sets up a reply with a negative answer in it for the client.
+// On return from mDNS_Execute, the client's reply structure is verified to be set as
+// expected. Lastly the timeout is simulated and mDNS_Execute is called. This results
+// in a call to TimeoutQuestions(). And again, the GenerateNegativeResponse() is called
+// which returns a negative response to the client.  This time the client reply is verified
+// to be setup with a timeout result.
+- (void)testStartLocalOnlyClientQueryRequest
+{
+       mDNS *const m = &mDNSStorage;
+    request_state* req = client_request_message;
+       char *msgptr = (char *)query_req_msgbuf;
+       size_t msgsz = sizeof(query_req_msgbuf);
+       DNSQuestion *q;
+       mDNSs32 min_size = sizeof(DNSServiceFlags) + sizeof(mDNSu32) + 4;
+       mStatus err = mStatus_NoError;
+       char qname_cstr[MAX_ESCAPED_DOMAIN_NAME];
+       struct reply_state *reply;
+       size_t len;
+
+       // Process the unit test's client request
+       start_client_request(req, msgptr, msgsz, query_request, local_socket);
+       XCTAssertEqual(err, mStatus_NoError);
+
+       // Verify the query initialized and request fields were set as expected
+       XCTAssertEqual(req->hdr.version, VERSION);
+       XCTAssertGreaterThan((mDNSs32)req->data_bytes, min_size);
+       XCTAssertEqual(req->flags, (kDNSServiceFlagsSuppressUnusable | kDNSServiceFlagsReturnIntermediates | kDNSServiceFlagsTimeout));
+       XCTAssertEqual(req->interfaceIndex, kDNSServiceInterfaceIndexLocalOnly);
+    XCTAssertNotEqual(req->terminate, (req_termination_fn)0);
+
+       q = &req->u.queryrecord.op.q;
+       XCTAssertEqual(q, m->NewLocalOnlyQuestions);
+       XCTAssertNil((__bridge id)m->Questions);
+       XCTAssertNil((__bridge id)m->NewQuestions);
+       XCTAssertEqual(q->SuppressUnusable, 1);
+       XCTAssertEqual(q->ReturnIntermed, 1);
+       XCTAssertEqual(q->Suppressed, mDNSfalse);                                                                       // Regress <rdar://problem/27571734>
+
+       ConvertDomainNameToCString(&q->qname, qname_cstr);
+       XCTAssertFalse(strcmp(qname_cstr, domainname_cstr));
+       XCTAssertEqual(q->qnamehash, DomainNameHashValue(&q->qname));
+
+       XCTAssertEqual(q->InterfaceID, mDNSInterface_LocalOnly);
+       XCTAssertEqual(q->flags, req->flags);
+       XCTAssertEqual(q->qtype, 1);
+       XCTAssertEqual(q->qclass, 1);
+       XCTAssertEqual(q->LongLived, 0);
+       XCTAssertEqual(q->ExpectUnique, mDNSfalse);
+       XCTAssertEqual(q->ForceMCast, 0);
+       XCTAssertEqual(q->TimeoutQuestion, 1);
+       XCTAssertEqual(q->WakeOnResolve, 0);
+       XCTAssertEqual(q->UseBackgroundTraffic, 0);
+       XCTAssertEqual(q->ValidationRequired, 0);
+       XCTAssertEqual(q->ValidatingResponse, 0);
+       XCTAssertEqual(q->ProxyQuestion, 0);
+    XCTAssertNotEqual((void*)q->QuestionCallback, (void*)mDNSNULL);
+       XCTAssertNil((__bridge id)q->DNSSECAuthInfo);
+    XCTAssertNil((__bridge id)(void*)q->DAIFreeCallback);
+       XCTAssertNotEqual(q->StopTime, 0);
+       XCTAssertEqual(q->AppendSearchDomains, 0);
+    XCTAssertNil((__bridge id)q->DuplicateOf);
+
+       // At this point the the cache is empty. Calling mDNS_Execute will answer the local-only
+       // question with a negative response.
+       m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
+       mDNS_Execute(m);  // Regress <rdar://problem/28721294>
+
+       // Verify reply is a negative response and error code is set to kDNSServiceErr_NoSuchRecord error.
+       reply = req->replies;
+    XCTAssertNotEqual(reply, (reply_state*)mDNSNULL);
+
+       XCTAssertNil((__bridge id)m->NewLocalOnlyQuestions);
+       XCTAssertEqual(q->LOAddressAnswers, 0);
+
+       len = get_reply_len(qname_cstr, 0);
+
+       XCTAssertNil((__bridge id)reply->next);
+       XCTAssertEqual(reply->totallen, reply->mhdr->datalen + sizeof(ipc_msg_hdr));
+       XCTAssertEqual(reply->mhdr->version, VERSION);
+       XCTAssertEqual(reply->mhdr->datalen, len);
+       XCTAssertEqual(reply->mhdr->ipc_flags, 0);
+       XCTAssertEqual(reply->mhdr->op, query_reply_op);
+
+    XCTAssertTrue((reply->rhdr->flags & htonl(kDNSServiceFlagsAdd)));
+       XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexLocalOnly);       // Regress <rdar://problem/27340874>
+       XCTAssertEqual(reply->rhdr->error,
+                                       (DNSServiceErrorType)htonl(kDNSServiceErr_NoSuchRecord));       // Regress <rdar://problem/24827555>
+
+       // Simulate what udsserver_idle normally does for clean up
+       freeL("StartLocalOnlyClientQueryRequest:reply", reply);
+       req->replies = NULL;
+
+       // Simulate the query time out of the local-only question.
+       // The expected behavior is a negative answer with time out error
+       m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
+       q->StopTime = mDNS_TimeNow_NoLock(m);
+       m->NextScheduledStopTime -= mDNSPlatformOneSecond*5;
+       mDNS_Execute(m);
+
+       // Verify the reply is a negative response with timeout error.
+       reply = req->replies;
+    XCTAssertNotEqual(reply, (reply_state*)mDNSNULL);
+    XCTAssertNil((__bridge id)m->NewLocalOnlyQuestions);
+    XCTAssertEqual(q->LOAddressAnswers, 0);
+
+       len = get_reply_len(qname_cstr, 0);
+
+    XCTAssertNil((__bridge id)reply->next);
+       XCTAssertEqual(reply->totallen, len + sizeof(ipc_msg_hdr));
+       XCTAssertEqual(reply->mhdr->version, VERSION);
+       XCTAssertEqual(reply->mhdr->datalen, len);
+       XCTAssertEqual(reply->mhdr->ipc_flags, 0);
+       XCTAssertEqual(reply->mhdr->op, query_reply_op);
+    XCTAssertTrue((reply->rhdr->flags & htonl(kDNSServiceFlagsAdd)));
+       XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexLocalOnly);       // Regress <rdar://problem/27340874>
+       XCTAssertEqual(reply->rhdr->error,
+                                       (DNSServiceErrorType)htonl(kDNSServiceErr_Timeout));            // Regress <rdar://problem/27562965>
+
+       // Free request and reallocate to use when query is restarted
+       free_req(req);
+       client_request_message = calloc(1, sizeof(request_state));
+}
+
+// This unit test populates the cache with four /etc/hosts records and then
+// verifies there are four entries in the cache.
+- (void)testPopulateCacheWithClientLOResponseRecords
+{
+       mDNS *const m = &mDNSStorage;
+
+       // Verify cache is empty
+       int count = LogEtcHosts_ut(m);
+       XCTAssertEqual(count, 0);
+
+       // Populate /etc/hosts
+       mStatus result = InitEtcHostsRecords();
+       XCTAssertEqual(result, mStatus_NoError);
+
+       // mDNS_Execute is called to populate the /etc/hosts cache.
+       m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
+       mDNS_Execute(m);
+
+       count = LogEtcHosts_ut(m);
+       XCTAssertEqual(count, 4);
+    
+    [self _testRestartLocalOnlyClientQueryRequest];   //  Continuation of this test
+}
+
+// This unit test starts a local only request for "cardinal2.apple.com.".  It first
+// calls start_client_request to start a query, it then verifies the
+// req and query data structures are set as expected. Next, the cache is verified to
+// contain the answer by AnswerNewLocalOnlyQuestion() and so results in setting up an
+// answer reply to the client. On return from mDNS_Execute, the client's reply structure
+// is verified to be set as expected. Lastly the timeout is simulated and mDNS_Execute is
+// called. This results in a call to TimeoutQuestions(). And this time, the
+// GenerateNegativeResponse() is called which returns a negative response to the client
+// which specifies the timeout occurred. Again, the answer reply is verified to
+// to specify a timeout.
+- (void)_testRestartLocalOnlyClientQueryRequest
+{
+       mDNS *const m = &mDNSStorage;
+       request_state* req = client_request_message;
+       char *msgptr = (char *)query_req_msgbuf;
+       size_t msgsz = sizeof(query_req_msgbuf);        DNSQuestion *q;
+       mDNSs32 min_size = sizeof(DNSServiceFlags) + sizeof(mDNSu32) + 4;
+       mStatus err = mStatus_NoError;
+       char qname_cstr[MAX_ESCAPED_DOMAIN_NAME];
+       struct reply_state *reply;
+       size_t len;
+
+       // Process the unit test's client request
+       start_client_request(req, msgptr, msgsz, query_request, local_socket);
+    XCTAssertEqual(err, mStatus_NoError);
+
+       XCTAssertEqual(req->hdr.version, VERSION);
+    XCTAssertGreaterThan((mDNSs32)req->data_bytes, min_size);
+       XCTAssertEqual(req->flags, (kDNSServiceFlagsSuppressUnusable | kDNSServiceFlagsReturnIntermediates | kDNSServiceFlagsTimeout));
+       XCTAssertEqual(req->interfaceIndex, kDNSServiceInterfaceIndexLocalOnly);
+    XCTAssertNotEqual(req->terminate, (req_termination_fn)0);
+    XCTAssertNil((__bridge id)m->Questions);
+
+       q = &req->u.queryrecord.op.q;
+       XCTAssertEqual(q, m->NewLocalOnlyQuestions);
+       XCTAssertEqual(q->SuppressUnusable, 1);
+       XCTAssertEqual(q->ReturnIntermed, 1);
+       XCTAssertEqual(q->Suppressed, mDNSfalse);                                                                               // Regress <rdar://problem/27571734>
+       XCTAssertEqual(q->qnamehash, DomainNameHashValue(&q->qname));
+       XCTAssertEqual(q->InterfaceID, mDNSInterface_LocalOnly);
+       XCTAssertEqual(q->flags, req->flags);
+       XCTAssertEqual(q->qtype, 1);
+       XCTAssertEqual(q->qclass, 1);
+       XCTAssertEqual(q->LongLived, 0);
+       XCTAssertEqual(q->ExpectUnique, mDNSfalse);
+       XCTAssertEqual(q->ForceMCast, 0);
+       XCTAssertEqual(q->TimeoutQuestion, 1);
+       XCTAssertEqual(q->WakeOnResolve, 0);
+       XCTAssertEqual(q->UseBackgroundTraffic, 0);
+       XCTAssertEqual(q->ValidationRequired, 0);
+       XCTAssertEqual(q->ValidatingResponse, 0);
+       XCTAssertEqual(q->ProxyQuestion, 0);
+    XCTAssertNotEqual((void*)q->QuestionCallback, (void*)mDNSNULL);
+    XCTAssertNil((__bridge id)q->DNSSECAuthInfo);
+    XCTAssertNil((__bridge id)(void*)q->DAIFreeCallback);
+    XCTAssertNotEqual(q->StopTime, 0);
+    XCTAssertEqual(q->AppendSearchDomains, 0);
+    XCTAssertNil((__bridge id)q->DuplicateOf);
+       ConvertDomainNameToCString(&q->qname, qname_cstr);
+    XCTAssertFalse(strcmp(qname_cstr, domainname_cstr));
+
+       // Answer local-only question with found cache entry
+       m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
+       mDNS_Execute(m);                                                                                                                        // Regress <rdar://problem/28721294>
+    XCTAssertNil((__bridge id)m->NewLocalOnlyQuestions);
+       XCTAssertEqual(req->u.queryrecord.op.answered, 1);
+       XCTAssertEqual(q->LOAddressAnswers, 1);
+       XCTAssertEqual(q, m->LocalOnlyQuestions);
+
+       reply = req->replies;
+       len = get_reply_len(qname_cstr, 4);
+
+    XCTAssertNil((__bridge id)reply->next);
+       XCTAssertEqual(reply->totallen, len + sizeof(ipc_msg_hdr));
+       XCTAssertEqual(reply->mhdr->version, VERSION);
+       XCTAssertEqual(reply->mhdr->datalen, len);
+       XCTAssertEqual(reply->mhdr->ipc_flags, 0);
+       XCTAssertEqual(reply->mhdr->op, query_reply_op);
+       XCTAssertTrue((reply->rhdr->flags & htonl(kDNSServiceFlagsAdd)));
+       XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexLocalOnly);       // Regress <rdar://problem/27340874>
+       XCTAssertEqual(reply->rhdr->error, kDNSServiceErr_NoError);
+
+       // Simulate the query time out of the local-only question.
+       // The expected behavior is a negative answer with time out error
+       m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
+       q->StopTime = mDNS_TimeNow_NoLock(m);
+       m->NextScheduledStopTime -= mDNSPlatformOneSecond*5;
+       mDNS_Execute(m);
+
+       reply = req->replies->next;
+       XCTAssertNotEqual(reply, (reply_state*)mDNSNULL);
+    XCTAssertNil((__bridge id)reply->next);
+    XCTAssertNil((__bridge id)m->NewLocalOnlyQuestions);
+       XCTAssertEqual(q->LOAddressAnswers, 0);
+       len = get_reply_len(qname_cstr, 0);
+
+    XCTAssertNil((__bridge id)reply->next);
+       XCTAssertEqual(reply->totallen, len + + sizeof(ipc_msg_hdr));
+       XCTAssertEqual(reply->mhdr->version, VERSION);
+       XCTAssertEqual(reply->mhdr->datalen, len);
+       XCTAssertEqual(reply->mhdr->ipc_flags, 0);
+       XCTAssertEqual(reply->mhdr->op, query_reply_op);
+    XCTAssertTrue((reply->rhdr->flags & htonl(kDNSServiceFlagsAdd)));
+       XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexLocalOnly);       // Regress <rdar://problem/27340874>
+       XCTAssertEqual(reply->rhdr->error,
+                                       (DNSServiceErrorType)htonl(kDNSServiceErr_Timeout));            // Regress <rdar://problem/27562965>
+
+       free_req(req);
+}
+
+@end
diff --git a/mDNSMacOSX/Tests/Unit Tests/LocalOnlyWithInterfacesTest.m b/mDNSMacOSX/Tests/Unit Tests/LocalOnlyWithInterfacesTest.m
new file mode 100644 (file)
index 0000000..07656c4
--- /dev/null
@@ -0,0 +1,399 @@
+/*
+ * Copyright (c) 2019 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "unittest_common.h"
+#import <XCTest/XCTest.h>
+
+struct UDPSocket_struct
+{
+       mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port
+};
+typedef struct UDPSocket_struct UDPSocket;
+
+// This client request was generated using the following command: "dns-sd -Q 123server.dotbennu.com. A".
+char test_query_any_msgbuf[35] = {
+    0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x32, 0x33, 0x73, 0x65, 0x72, 0x76, 0x65,
+    0x72, 0x2e, 0x64, 0x6f, 0x74, 0x62, 0x65, 0x6e, 0x6e, 0x75, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00,
+    0x01, 0x00, 0x01
+};
+
+// Modified for different scopes
+char test_query_local_msgbuf[35] = {
+    0x00, 0x00, 0x10, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x31, 0x32, 0x33, 0x73, 0x65, 0x72, 0x76, 0x65,
+    0x72, 0x2e, 0x64, 0x6f, 0x74, 0x62, 0x65, 0x6e, 0x6e, 0x75, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00,
+    0x01, 0x00, 0x01
+};
+
+char test_query_interface_msgbuf[35] = {
+    0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x32, 0x33, 0x73, 0x65, 0x72, 0x76, 0x65,
+    0x72, 0x2e, 0x64, 0x6f, 0x74, 0x62, 0x65, 0x6e, 0x6e, 0x75, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00,
+    0x01, 0x00, 0x01
+};
+
+// Variables associated with contents of the above uDNS message
+mDNSlocal char test_domainname_cstr[] = "123server.dotbennu.com.";
+
+mDNSlocal mDNSBool _TestCreateEtcHostsEntryWithInterfaceID(const domainname *domain, const struct sockaddr *sa, const domainname *cname, mDNSInterfaceID interfaceID, AuthHash *auth)
+{   // Copied from mDNSMacOSXCreateEtcHostsEntry
+    AuthRecord *rr;
+    mDNSu32 namehash;
+    AuthGroup *ag;
+    mDNSInterfaceID InterfaceID = mDNSInterface_LocalOnly;
+    mDNSu16 rrtype;
+
+    if (!domain)
+    {
+        LogMsg("_TestCreateEtcHostsEntryWithInterfaceID: ERROR!! name NULL");
+        return mDNSfalse;
+    }
+    if (!sa && !cname)
+    {
+        LogMsg("_TestCreateEtcHostsEntryWithInterfaceID: ERROR!! sa and cname both NULL");
+        return mDNSfalse;
+    }
+
+    if (sa && sa->sa_family != AF_INET && sa->sa_family != AF_INET6)
+    {
+        LogMsg("_TestCreateEtcHostsEntryWithInterfaceID: ERROR!! sa with bad family %d", sa->sa_family);
+        return mDNSfalse;
+    }
+
+
+    if (interfaceID)
+    {
+        InterfaceID = interfaceID;
+    }
+
+    if (sa)
+        rrtype = (sa->sa_family == AF_INET ? kDNSType_A : kDNSType_AAAA);
+    else
+        rrtype = kDNSType_CNAME;
+
+    // Check for duplicates. See whether we parsed an entry before like this ?
+    namehash = DomainNameHashValue(domain);
+    ag = AuthGroupForName(auth, namehash, domain);
+    if (ag)
+    {
+        rr = ag->members;
+        while (rr)
+        {
+            if (rr->resrec.rrtype == rrtype)
+            {
+                if (rrtype == kDNSType_A)
+                {
+                    mDNSv4Addr ip;
+                    ip.NotAnInteger = ((struct sockaddr_in*)sa)->sin_addr.s_addr;
+                    if (mDNSSameIPv4Address(rr->resrec.rdata->u.ipv4, ip) && InterfaceID == rr->resrec.InterfaceID)
+                    {
+                        LogInfo("_TestCreateEtcHostsEntryWithInterfaceID: Same IPv4 address and InterfaceID for name %##s ID %d", domain->c, IIDPrintable(InterfaceID));
+                        return mDNSfalse;
+                    }
+                }
+                else if (rrtype == kDNSType_AAAA)
+                {
+                    mDNSv6Addr ip6;
+                    ip6.l[0] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[0];
+                    ip6.l[1] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[1];
+                    ip6.l[2] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[2];
+                    ip6.l[3] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[3];
+                    if (mDNSSameIPv6Address(rr->resrec.rdata->u.ipv6, ip6) && InterfaceID == rr->resrec.InterfaceID)
+                    {
+                        LogInfo("_TestCreateEtcHostsEntryWithInterfaceID: Same IPv6 address and InterfaceID for name %##s ID %d", domain->c, IIDPrintable(InterfaceID));
+                        return mDNSfalse;
+                    }
+                }
+                else if (rrtype == kDNSType_CNAME)
+                {
+                    if (SameDomainName(&rr->resrec.rdata->u.name, cname))
+                    {
+                        LogInfo("_TestCreateEtcHostsEntryWithInterfaceID: Same cname %##s for name %##s", cname->c, domain->c);
+                        return mDNSfalse;
+                    }
+                }
+            }
+            rr = rr->next;
+        }
+    }
+    rr = (AuthRecord *) callocL("etchosts", sizeof(*rr));
+    if (rr == NULL) return mDNSfalse;
+    mDNS_SetupResourceRecord(rr, NULL, InterfaceID, rrtype, 1, kDNSRecordTypeKnownUnique, AuthRecordLocalOnly, FreeEtcHosts, NULL);
+    AssignDomainName(&rr->namestorage, domain);
+
+    if (sa)
+    {
+        rr->resrec.rdlength = sa->sa_family == AF_INET ? sizeof(mDNSv4Addr) : sizeof(mDNSv6Addr);
+        if (sa->sa_family == AF_INET)
+            rr->resrec.rdata->u.ipv4.NotAnInteger = ((struct sockaddr_in*)sa)->sin_addr.s_addr;
+        else
+        {
+            rr->resrec.rdata->u.ipv6.l[0] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[0];
+            rr->resrec.rdata->u.ipv6.l[1] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[1];
+            rr->resrec.rdata->u.ipv6.l[2] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[2];
+            rr->resrec.rdata->u.ipv6.l[3] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[3];
+        }
+    }
+    else
+    {
+        rr->resrec.rdlength = DomainNameLength(cname);
+        rr->resrec.rdata->u.name.c[0] = 0;
+        AssignDomainName(&rr->resrec.rdata->u.name, cname);
+    }
+    rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
+    SetNewRData(&rr->resrec, mDNSNULL, 0);  // Sets rr->rdatahash for us
+    LogInfo("_TestCreateEtcHostsEntryWithInterfaceID: Adding resource record %s ID %d", ARDisplayString(&mDNSStorage, rr), IIDPrintable(rr->resrec.InterfaceID));
+    InsertAuthRecord(&mDNSStorage, auth, rr);
+    return mDNStrue;
+}
+
+mDNSlocal mStatus InitEtcHostsRecords(void)
+{
+    mDNS *m = &mDNSStorage;
+    struct sockaddr_storage hostaddr;
+    domainname domain;
+
+    AuthHash newhosts;
+    mDNSPlatformMemZero(&newhosts, sizeof(AuthHash));
+
+    memset(&hostaddr, 0, sizeof(hostaddr));
+    get_ip("10.0.0.201", &hostaddr);
+    MakeDomainNameFromDNSNameString(&domain, "123server.dotbennu.com");
+    _TestCreateEtcHostsEntryWithInterfaceID(&domain, (struct sockaddr *) &hostaddr, mDNSNULL, mDNSInterface_P2P, &newhosts);
+
+    memset(&hostaddr, 0, sizeof(hostaddr));
+    get_ip("10.0.0.202", &hostaddr);
+    MakeDomainNameFromDNSNameString(&domain, "123server.dotbennu.com");
+    mDNSMacOSXCreateEtcHostsEntry_ut(&domain, (struct sockaddr *) &hostaddr, mDNSNULL, mDNSNULL, &newhosts);
+
+    memset(&hostaddr, 0, sizeof(hostaddr));
+    get_ip("10.0.0.203", &hostaddr);
+    MakeDomainNameFromDNSNameString(&domain, "123server.dotbennu.com");
+    _TestCreateEtcHostsEntryWithInterfaceID(&domain, (struct sockaddr *) &hostaddr, mDNSNULL, primary_interfaceID, &newhosts);
+
+    UpdateEtcHosts_ut(&newhosts);
+    m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
+    mDNS_Execute(m);
+
+    return mStatus_NoError;
+}
+
+mDNSlocal mDNSs32 NumReplies(reply_state * reply)
+{
+    mDNSs32 result = 0;
+    reply_state * nextreply = reply;
+    while(nextreply) { result++; nextreply = nextreply->next;}
+    return result;
+}
+
+mDNSlocal mDNSBool HasReplyWithInterfaceIndex(reply_state * reply, mDNSu32 interfaceIndex)
+{
+    mDNSBool result = mDNSfalse;
+    reply_state * nextreply = reply;
+    while(nextreply)
+    {
+        result = (ntohl(nextreply->rhdr[0].ifi) == interfaceIndex);
+        if (result) break;
+        nextreply = nextreply->next;
+    }
+    return result;
+}
+
+@interface LocalOnlyWithInterfacesTest : XCTestCase
+{
+    UDPSocket* local_socket;
+    request_state* client_request_message;}
+@end
+
+@implementation LocalOnlyWithInterfacesTest
+
+// The InitThisUnitTest() initializes the mDNSResponder environment as well as
+// a DNSServer. It also allocates memory for a local_socket and client request.
+// Note: This unit test does not send packets on the wire and it does not open sockets.
+- (void)setUp
+{
+    mDNSPlatformMemZero(&mDNSStorage, sizeof(mDNS));
+
+    // Init unit test environment and verify no error occurred.
+    mStatus result = init_mdns_environment(mDNStrue);
+    XCTAssertEqual(result, mStatus_NoError);
+    
+    // Add one DNS server and verify it was added.
+    AddDNSServer_ut();
+    XCTAssertEqual(CountOfUnicastDNSServers(&mDNSStorage), 1);
+
+    AddDNSServerScoped_ut(primary_interfaceID, kScopeInterfaceID);
+    XCTAssertEqual(CountOfUnicastDNSServers(&mDNSStorage), 2);
+
+    // Populate /etc/hosts
+    result = InitEtcHostsRecords();
+    XCTAssertEqual(result, mStatus_NoError);
+    
+    int count = LogEtcHosts_ut(&mDNSStorage);
+    XCTAssertEqual(count, 3);
+
+    // Create memory for a socket that is never used or opened.
+    local_socket = (UDPSocket *) mDNSPlatformMemAllocateClear(sizeof(*local_socket));
+    
+    // Create memory for a request that is used to make this unit test's client request.
+    client_request_message = calloc(1, sizeof(request_state));
+}
+
+- (void)tearDown
+{
+    mDNS *m = &mDNSStorage;
+    request_state* req = client_request_message;
+    DNSServer   *ptr, **p = &m->DNSServers;
+    
+    while (req->replies)
+    {
+        reply_state *reply = req->replies;
+        req->replies = req->replies->next;
+        mDNSPlatformMemFree(reply);
+    }
+    mDNSPlatformMemFree(req);
+    
+    mDNSPlatformMemFree(local_socket);
+    
+    while (*p)
+    {
+        ptr = *p;
+        *p = (*p)->next;
+        LogInfo("FinalizeUnitTest: Deleting server %p %#a:%d (%##s)", ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c);
+        mDNSPlatformMemFree(ptr);
+    }
+}
+
+// This unit test tries 3 different local cache queries
+// 1) Test the Any query does not receive the entry scoped to the primary interface, but does received the local and P2P entries
+// 2) Test the LocalOnly query receives all the entries
+// 3) Test the interface scoped query receives the interface scoped entry
+- (void)testLocalOnlyWithInterfacesTestSeries
+{
+    request_state* req = client_request_message;
+
+    // Verify Any index returns 2 results.
+    [self _executeClientQueryRequest: req andMsgBuf: test_query_any_msgbuf];
+    XCTAssertEqual(NumReplies(req->replies), 2);
+    XCTAssertTrue(HasReplyWithInterfaceIndex(req->replies, kDNSServiceInterfaceIndexP2P));
+    XCTAssertTrue(HasReplyWithInterfaceIndex(req->replies, kDNSServiceInterfaceIndexLocalOnly));
+
+    // Verify LocalOnly index returns 3 results.
+    [self _executeClientQueryRequest: req andMsgBuf: test_query_local_msgbuf];
+    XCTAssertEqual(NumReplies(req->replies), 3);
+    XCTAssertTrue(HasReplyWithInterfaceIndex(req->replies, kDNSServiceInterfaceIndexP2P));
+    XCTAssertTrue(HasReplyWithInterfaceIndex(req->replies, kDNSServiceInterfaceIndexLocalOnly));
+    XCTAssertTrue(HasReplyWithInterfaceIndex(req->replies, primary_interfaceID));
+
+    // Verify en0 index returns 1 result.
+    test_query_interface_msgbuf[7] = primary_interfaceID;
+    [self _executeClientQueryRequest: req andMsgBuf: test_query_interface_msgbuf];
+    XCTAssertEqual(NumReplies(req->replies), 1);
+    XCTAssertTrue(HasReplyWithInterfaceIndex(req->replies, primary_interfaceID));
+
+}
+
+// Simulate a uds client request by setting up a client request and then
+// calling mDNSResponder's handle_client_request.  The handle_client_request function
+// processes the request and starts a query.  This unit test verifies
+// the client request and query were setup as expected.  This unit test also calls
+// mDNS_execute which determines the cache does not contain the new question's
+// answer.
+- (void)_executeClientQueryRequest: (request_state*)req andMsgBuf: (char*)msgbuf
+{
+    mDNS *const m = &mDNSStorage;
+    char *msgptr = msgbuf;
+    size_t msgsz = sizeof(test_query_local_msgbuf);
+    mDNSs32 min_size = sizeof(DNSServiceFlags) + sizeof(mDNSu32) + 4;
+    DNSQuestion *q;
+    mStatus err = mStatus_NoError;
+    char qname_cstr[MAX_ESCAPED_DOMAIN_NAME];
+
+    // Process the unit test's client request
+    start_client_request(req, msgptr, msgsz, query_request, local_socket);
+    XCTAssertEqual(err, mStatus_NoError);
+    
+    // Verify the request fields were set as expected
+    XCTAssertNil((__bridge id)req->next);
+    XCTAssertNil((__bridge id)req->primary);
+    XCTAssertEqual(req->sd, client_req_sd);
+    XCTAssertEqual(req->process_id, client_req_process_id);
+    XCTAssertFalse(strcmp(req->pid_name, client_req_pid_name));
+    XCTAssertEqual(req->validUUID, mDNSfalse);
+    XCTAssertEqual(req->errsd, 0);
+    XCTAssertEqual(req->uid, client_req_uid);
+    XCTAssertEqual(req->ts, t_complete);
+    XCTAssertGreaterThan((mDNSs32)req->data_bytes, min_size);
+    XCTAssertEqual(req->msgend, msgptr+msgsz);
+    XCTAssertNil((__bridge id)(void*)req->msgbuf);
+    XCTAssertEqual(req->hdr.version, VERSION);
+    XCTAssertNil((__bridge id)req->replies);
+    XCTAssertNotEqual(req->terminate, (req_termination_fn)0);
+    XCTAssertEqual(req->flags, kDNSServiceFlagsReturnIntermediates);
+    
+    // Verify the query fields were set as expected
+    q = &req->u.queryrecord.op.q;
+    XCTAssertNotEqual(q, (DNSQuestion *)mDNSNULL);
+    if (m->Questions)
+    {
+        XCTAssertEqual(q, m->Questions);
+        XCTAssertEqual(q, m->NewQuestions);
+        XCTAssertTrue(q->InterfaceID == mDNSInterface_Any || q->InterfaceID == primary_interfaceID);
+    }
+    else
+    {
+        XCTAssertEqual(q, m->LocalOnlyQuestions);
+        XCTAssertEqual(q, m->NewLocalOnlyQuestions);
+        XCTAssertEqual(q->InterfaceID, mDNSInterface_LocalOnly);
+    }
+    XCTAssertEqual(q->SuppressUnusable, mDNSfalse);
+    XCTAssertEqual(q->ReturnIntermed, mDNStrue);
+    XCTAssertEqual(q->Suppressed, mDNSfalse);
+    
+    ConvertDomainNameToCString(&q->qname, qname_cstr);
+    XCTAssertFalse(strcmp(qname_cstr, test_domainname_cstr));
+    XCTAssertEqual(q->qnamehash, DomainNameHashValue(&q->qname));
+    
+    XCTAssertEqual(q->flags, req->flags);
+    XCTAssertEqual(q->qtype, 1);
+    XCTAssertEqual(q->qclass, 1);
+    XCTAssertEqual(q->LongLived, 0);
+    XCTAssertEqual(q->ExpectUnique, mDNSfalse);
+    XCTAssertEqual(q->ForceMCast, 0);
+    XCTAssertEqual(q->TimeoutQuestion, 0);
+    XCTAssertEqual(q->WakeOnResolve, 0);
+    XCTAssertEqual(q->UseBackgroundTraffic, 0);
+    XCTAssertEqual(q->ValidationRequired, 0);
+    XCTAssertEqual(q->ValidatingResponse, 0);
+    XCTAssertEqual(q->ProxyQuestion, 0);
+    XCTAssertNotEqual((void*)q->QuestionCallback, (void*)mDNSNULL);
+    XCTAssertNil((__bridge id)q->DNSSECAuthInfo);
+    XCTAssertNil((__bridge id)(void*)q->DAIFreeCallback);
+    XCTAssertEqual(q->AppendSearchDomains, 0);
+    XCTAssertNil((__bridge id)q->DuplicateOf);
+    
+    // Call mDNS_Execute to see if the new question, q, has an answer in the cache.
+    // It won't be yet because the cache is empty.
+    m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
+    mDNS_Execute(m);
+
+    // Verify mDNS_Execute processed the new question.
+    XCTAssertNil((__bridge id)m->NewQuestions);
+    XCTAssertNil((__bridge id)m->NewLocalOnlyQuestions);
+    XCTAssertEqual(m->rrcache_totalused, 0);
+    m->Questions = nil;                 //  Reset
+}
+
+
+@end
diff --git a/mDNSMacOSX/Tests/Unit Tests/ResourceRecordTest.m b/mDNSMacOSX/Tests/Unit Tests/ResourceRecordTest.m
new file mode 100644 (file)
index 0000000..8a71f43
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2017-2018 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mDNSEmbeddedAPI.h"
+#include "DNSCommon.h"
+#import <XCTest/XCTest.h>
+
+@interface ResourceRecordTest : XCTestCase
+{
+}
+@end
+
+@implementation ResourceRecordTest
+
+- (void)setUp
+{
+}
+
+- (void)tearDown
+{
+}
+
+- (void)testTXTSetup
+{
+    AuthRecord authRec;
+    mDNS_SetupResourceRecord(&authRec, mDNSNULL, mDNSInterface_Any, kDNSType_TXT, kStandardTTL, kDNSRecordTypeShared, AuthRecordAny,mDNSNULL, mDNSNULL);
+    XCTAssertEqual(authRec.resrec.rrtype,               kDNSType_TXT);
+    XCTAssertEqual(authRec.resrec.RecordType,           kDNSRecordTypeShared);
+    XCTAssertEqual(authRec.resrec.rdata->MaxRDLength,   sizeof(RDataBody));
+}
+
+- (void)testASetup
+{
+    AuthRecord authRec;
+    mDNS_SetupResourceRecord(&authRec, mDNSNULL, mDNSInterface_Any, kDNSType_A, kHostNameTTL, kDNSRecordTypeUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
+    
+    XCTAssertEqual(authRec.resrec.rrtype,           kDNSType_A);
+    XCTAssertEqual(authRec.resrec.RecordType,       kDNSRecordTypeUnique);
+    // Add more verifications
+}
+
+- (void)testOPTSetup
+{
+    AuthRecord opt;
+    mDNSu32    updatelease = 7200;
+
+    // Setup the OPT Record
+    mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
+
+    // Verify the basic initialization is all ok
+
+    opt.resrec.rrclass    = NormalMaxDNSMessageData;
+    opt.resrec.rdlength   = sizeof(rdataOPT);   // One option in this OPT record
+    opt.resrec.rdestimate = sizeof(rdataOPT);
+    opt.resrec.rdata->u.opt[0].opt           = kDNSOpt_Lease;
+    opt.resrec.rdata->u.opt[0].u.updatelease = updatelease;
+
+    // Put the resource record in and verify everything is fine
+#if 0
+    mDNSu8     data[AbsoluteMaxDNSMessageData];
+    mDNSu8     *p = data;
+    mDNSu16    numAdditionals;
+    
+    p = PutResourceRecordTTLWithLimit((DNSMessage*)&data, p, &numAdditionals, &opt.resrec, opt.resrec.rroriginalttl, data + AbsoluteMaxDNSMessageData);
+#endif
+}
+
+// Repeat with bad data to make sure it bails out cleanly
+
+#if 0
+- (void)testPerformanceExample {
+    // This is an example of a performance test case.
+    [self measureBlock:^{
+        // Put the code you want to measure the time of here.
+    }];
+}
+#endif
+
+@end
diff --git a/mDNSMacOSX/Tests/Unit Tests/SuspiciousReplyTest.m b/mDNSMacOSX/Tests/Unit Tests/SuspiciousReplyTest.m
new file mode 100644 (file)
index 0000000..534bbb8
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2017-2019 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "unittest_common.h"
+#import <XCTest/XCTest.h>
+
+struct UDPSocket_struct
+{
+       mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port
+};
+typedef struct UDPSocket_struct UDPSocket;
+
+// This client request was generated using the following command: "dns-sd -Q 123server.dotbennu.com. A".
+uint8_t test_query_client_msgbuf[35] = {
+       0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x32, 0x33, 0x73, 0x65, 0x72, 0x76, 0x65,
+       0x72, 0x2e, 0x64, 0x6f, 0x74, 0x62, 0x65, 0x6e, 0x6e, 0x75, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00,
+       0x01, 0x00, 0x01
+};
+
+// This uDNS message is a canned response that was originally captured by wireshark.
+uint8_t test_query_response_msgbuf[108] = {
+    0x69, 0x41, // transaction id
+       0x85, 0x80, // flags
+       0x00, 0x01, // 1 question for 123server.dotbennu.com. Addr
+       0x00, 0x02,     // 2 anwsers: 123server.dotbennu.com. CNAME test212.dotbennu.com., test212.dotbennu.com. Addr 10.100.0.1,
+       0x00, 0x01,     // 1 authorities anwser: dotbennu.com. NS cardinal2.apple.com.
+       0x00, 0x00, 0x09, 0x31, 0x32, 0x33,
+    0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x08, 0x64, 0x6f, 0x74, 0x62, 0x65, 0x6e, 0x6e, 0x75, 0x03,
+    0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00,
+    0x02, 0x56, 0x00, 0x0a, 0x07, 0x74, 0x65, 0x73, 0x74, 0x32, 0x31, 0x32, 0xc0, 0x16, 0xc0, 0x34,
+    0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x04, 0x0a, 0x64, 0x00, 0x01, 0xc0, 0x16,
+    0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0x51, 0x80, 0x00, 0x12, 0x09, 0x63, 0x61, 0x72, 0x64, 0x69,
+    0x6e, 0x61, 0x6c, 0x32, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x65, 0xc0, 0x1f
+};
+
+// Variables associated with contents of the above uDNS message
+#define uDNS_TargetQID 16745
+char test_original_domainname_cstr[] = "123server.dotbennu.com.";
+char test_cname_domainname_cstr[] = "test212.dotbennu.com.";
+
+@interface SuspiciousReplyTest : XCTestCase
+{
+    UDPSocket* local_socket;
+    request_state* client_request_message;}
+@end
+
+@implementation SuspiciousReplyTest
+
+// The InitThisUnitTest() initializes the mDNSResponder environment as well as
+// a DNSServer. It also allocates memory for a local_socket and client request.
+// Note: This unit test does not send packets on the wire and it does not open sockets.
+- (void)setUp
+{
+    mDNSPlatformMemZero(&mDNSStorage, sizeof(mDNS));
+
+    // Init unit test environment and verify no error occurred.
+    mStatus result = init_mdns_environment(mDNStrue);
+    XCTAssertEqual(result, mStatus_NoError);
+    
+    // Add one DNS server and verify it was added.
+    AddDNSServer_ut();
+    XCTAssertEqual(CountOfUnicastDNSServers(&mDNSStorage), 1);
+    
+    // Create memory for a socket that is never used or opened.
+    local_socket = (UDPSocket *) mDNSPlatformMemAllocateClear(sizeof(*local_socket));
+    
+    // Create memory for a request that is used to make this unit test's client request.
+    client_request_message = calloc(1, sizeof(request_state));
+}
+
+- (void)tearDown
+{
+    mDNS *m = &mDNSStorage;
+    request_state* req = client_request_message;
+    DNSServer   *ptr, **p = &m->DNSServers;
+    
+    while (req->replies)
+    {
+        reply_state *reply = req->replies;
+        req->replies = req->replies->next;
+        mDNSPlatformMemFree(reply);
+    }
+    mDNSPlatformMemFree(req);
+    
+    mDNSPlatformMemFree(local_socket);
+    
+    while (*p)
+    {
+        ptr = *p;
+        *p = (*p)->next;
+        LogInfo("FinalizeUnitTest: Deleting server %p %#a:%d (%##s)", ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c);
+        mDNSPlatformMemFree(ptr);
+    }
+}
+
+- (void)testSuspiciousReplyTestSeries
+{
+    [self _clientQueryRequest];
+    [self _verifySuspiciousResponseBehavior];
+}
+
+// Simulate a uds client request by setting up a client request and then
+// calling mDNSResponder's handle_client_request.  The handle_client_request function
+// processes the request and starts a query.  This unit test verifies
+// the client request and query were setup as expected.  This unit test also calls
+// mDNS_execute which determines the cache does not contain the new question's
+// answer.
+- (void)_clientQueryRequest
+{
+    mDNS *const m = &mDNSStorage;
+    request_state* req = client_request_message;
+    char *msgptr = (char *)test_query_client_msgbuf;
+    size_t msgsz = sizeof(test_query_client_msgbuf);
+    mDNSs32 min_size = sizeof(DNSServiceFlags) + sizeof(mDNSu32) + 4;
+    DNSQuestion *q;
+    mStatus err = mStatus_NoError;
+    char qname_cstr[MAX_ESCAPED_DOMAIN_NAME];
+    
+    // Process the unit test's client request
+    start_client_request(req, msgptr, msgsz, query_request, local_socket);
+    XCTAssertEqual(err, mStatus_NoError);
+    
+    // Verify the request fields were set as expected
+    XCTAssertNil((__bridge id)req->next);
+    XCTAssertNil((__bridge id)req->primary);
+    XCTAssertEqual(req->sd, client_req_sd);
+    XCTAssertEqual(req->process_id, client_req_process_id);
+    XCTAssertFalse(strcmp(req->pid_name, client_req_pid_name));
+    XCTAssertEqual(req->validUUID, mDNSfalse);
+    XCTAssertEqual(req->errsd, 0);
+    XCTAssertEqual(req->uid, client_req_uid);
+    XCTAssertEqual(req->ts, t_complete);
+    XCTAssertGreaterThan((mDNSs32)req->data_bytes, min_size);
+    XCTAssertEqual(req->msgend, msgptr+msgsz);
+    XCTAssertNil((__bridge id)(void*)req->msgbuf);
+    XCTAssertEqual(req->hdr.version, VERSION);
+    XCTAssertNil((__bridge id)req->replies);
+    XCTAssertNotEqual(req->terminate, (req_termination_fn)0);
+    XCTAssertEqual(req->flags, kDNSServiceFlagsReturnIntermediates);
+    XCTAssertEqual(req->interfaceIndex, kDNSServiceInterfaceIndexAny);
+    
+    // Verify the query fields were set as expected
+    q = &req->u.queryrecord.op.q;
+    XCTAssertNotEqual(q, (DNSQuestion *)mDNSNULL);
+    XCTAssertEqual(q, m->Questions);
+    XCTAssertEqual(q, m->NewQuestions);
+    XCTAssertEqual(q->SuppressUnusable, mDNSfalse);
+    XCTAssertEqual(q->ReturnIntermed, mDNStrue);
+    XCTAssertEqual(q->Suppressed, mDNSfalse);
+    
+    ConvertDomainNameToCString(&q->qname, qname_cstr);
+    XCTAssertFalse(strcmp(qname_cstr, test_original_domainname_cstr));
+    XCTAssertEqual(q->qnamehash, DomainNameHashValue(&q->qname));
+    
+    XCTAssertEqual(q->InterfaceID, mDNSInterface_Any);
+    XCTAssertEqual(q->flags, req->flags);
+    XCTAssertEqual(q->qtype, 1);
+    XCTAssertEqual(q->qclass, 1);
+    XCTAssertEqual(q->LongLived, 0);
+    XCTAssertEqual(q->ExpectUnique, mDNSfalse);
+    XCTAssertEqual(q->ForceMCast, 0);
+    XCTAssertEqual(q->TimeoutQuestion, 0);
+    XCTAssertEqual(q->WakeOnResolve, 0);
+    XCTAssertEqual(q->UseBackgroundTraffic, 0);
+    XCTAssertEqual(q->ValidationRequired, 0);
+    XCTAssertEqual(q->ValidatingResponse, 0);
+    XCTAssertEqual(q->ProxyQuestion, 0);
+    XCTAssertNotEqual((void*)q->QuestionCallback, (void*)mDNSNULL);
+    XCTAssertNil((__bridge id)q->DNSSECAuthInfo);
+    XCTAssertNil((__bridge id)(void*)q->DAIFreeCallback);
+    XCTAssertEqual(q->AppendSearchDomains, 0);
+    XCTAssertNil((__bridge id)q->DuplicateOf);
+    
+    // Call mDNS_Execute to see if the new question, q, has an answer in the cache.
+    // It won't be yet because the cache is empty.
+    m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
+    mDNS_Execute(m);
+    
+    // Verify mDNS_Execute processed the new question.
+    XCTAssertNil((__bridge id)m->NewQuestions);
+    
+    // Verify the cache is empty and the request got no reply.
+    XCTAssertEqual(m->rrcache_totalused, 0);
+    XCTAssertNil((__bridge id)req->replies);
+}
+
+// This unit test tries to receive a response but changes the QID so it is ignored and can trigger suspicious mode
+// 1) Test a suspicious response is ignored, but if it was previously requested, then don't go into suspicious mode
+// 2) Test a suspicious response is ignored, and it does trigger suspicious mode
+// 3) Test a configuration change event will reset suspicious mode
+- (void)_verifySuspiciousResponseBehavior
+{
+    mDNS *const m = &mDNSStorage;
+    DNSMessage *msgptr = (DNSMessage *)test_query_response_msgbuf;
+    size_t msgsz = sizeof(test_query_response_msgbuf);
+    request_state* req = client_request_message;
+    mDNSOpaque16    suspiciousQID;
+
+    // 1)
+    // Receive and verify it is suspicious  (ignored response)
+    // But not too suspicious               (did NOT go into suspicious mode)
+
+    suspiciousQID.NotAnInteger = 0xDEAD;
+    receive_suspicious_response_ut(req, msgptr, msgsz, suspiciousQID, true);
+    
+    // Verify 0 records recevied
+    mDNSu32 CacheUsed =0, notUsed =0;
+    LogCacheRecords_ut(mDNS_TimeNow(m), &CacheUsed, &notUsed);
+    XCTAssertEqual(CacheUsed, 0);               // Verify 0 records recevied
+    XCTAssertFalse(m->NextSuspiciousTimeout);   // And NOT in suspicious mode
+
+    // 2)
+    // Receive and verify it is suspicious  (ignored response)
+    // And put itself in suspicious mode    (did go into suspicious mode)
+
+    receive_suspicious_response_ut(req, msgptr, msgsz, suspiciousQID, false);
+    LogCacheRecords_ut(mDNS_TimeNow(m), &CacheUsed, &notUsed);
+    XCTAssertEqual(CacheUsed, 0);               // Verify 0 records recevied
+    XCTAssertTrue(m->NextSuspiciousTimeout);    // And IS in suspicious mode
+
+    // 3)
+    // Verify suspicious mode is stopped when a configuration change occurs.
+
+    force_uDNS_SetupDNSConfig_ut(m);
+    XCTAssertFalse(m->NextSuspiciousTimeout);
+}
+
+
+@end
diff --git a/mDNSMacOSX/Tests/Unit Tests/mDNSCoreReceiveTest.m b/mDNSMacOSX/Tests/Unit Tests/mDNSCoreReceiveTest.m
new file mode 100644 (file)
index 0000000..96b546f
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2017-2018 Apple Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "unittest_common.h"
+#import <XCTest/XCTest.h>
+
+// This DNS message was gleaned from a uDNS query request packet that was captured with Wireshark.
+uint8_t udns_query_request_message[28] = {            // contains 1 question for www.f5.com
+    0x31, 0xca, // transaction id
+    0x01, 0x00,    // flags
+    0x00, 0x01,    // 1 question
+    0x00, 0x00,    // no anwsers
+    0x00, 0x00,    // no authoritative answers
+    0x00, 0x00, // no additionals
+    0x03, 0x77, 0x77, 0x77, 0x02, 0x66, 0x35, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01
+};
+
+// This DNS message was gleaned from a uDNS query request packet that was captured with Wireshark.
+// Then the header id (more specifically, the msg->h.id) was deliberately cleared to force code
+// path to traverse regression case, <rdar://problem/28556513>.
+uint8_t udns_query_request_message_with_invalid_id[28] = {  // contains 1 question for www.f5.com, msg->h.id = 0
+    0x00, 0x00,    // transaction id
+    0x01, 0x00, // flags
+    0x00, 0x01, // 1 question
+    0x00, 0x00, // no anwsers
+    0x00, 0x00, // no authoritative answers
+    0x00, 0x00, // no additionals
+    0x03, 0x77, 0x77, 0x77, 0x02, 0x66, 0x35, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01
+};
+
+uint8_t arp_request_packet[28] = {  // contains 1 question for www.f5.com, msg->h.id = 0
+    0x00, 0x01, // hardware type: enet
+    0x08, 0x00, // protocol type: IP
+    0x06, // hardware size
+    0x04, // Protcol size
+    0x00, 0x01, // opcode request
+    0x24, 0x01, 0xc7, 0x24, 0x35, 0x00, // Sender mac addr
+    0x11, 0xe2, 0x14, 0x01,    // Sender ip addr
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,    // target mac addr
+    0x11, 0xe2, 0x17, 0xbe    // target ip addr
+};
+
+mDNSlocal void InitmDNSStorage(mDNS *const m)
+{
+    memset(m, 0, sizeof(mDNS));
+}
+
+@interface mDNSCoreReceiveTest : XCTestCase
+{
+}
+@end
+
+@implementation mDNSCoreReceiveTest
+
+- (void)setUp
+{
+    mDNSPlatformTimeInit();
+    mDNS_LoggingEnabled = 0;
+    mDNS_PacketLoggingEnabled = 0;
+}
+
+- (void)tearDown {
+}
+
+- (void)testValidQueryReq
+{
+    mDNS *const m = &mDNSStorage;
+    mDNSAddr srcaddr, dstaddr;
+    mDNSIPPort srcport, dstport;
+    DNSMessage * msg;
+    const mDNSu8 * end;
+    
+    // Init unit test environment and verify no error occurred.
+    mStatus result = init_mdns_environment(mDNStrue);
+    XCTAssertEqual(result, mStatus_NoError);
+    
+    // Used random values for srcaddr and srcport
+    srcaddr.type       = mDNSAddrType_IPv4;
+    srcaddr.ip.v4.b[0] = 192;
+    srcaddr.ip.v4.b[1] = 168;
+    srcaddr.ip.v4.b[2] = 1;
+    srcaddr.ip.v4.b[3] = 10;
+    srcport.NotAnInteger = swap16((mDNSu16)53);
+    
+    // Used random values for dstaddr and dstport
+    dstaddr.type       = mDNSAddrType_IPv4;
+    dstaddr.ip.v4.b[0] = 192;
+    dstaddr.ip.v4.b[1] = 168;
+    dstaddr.ip.v4.b[2] = 1;
+    dstaddr.ip.v4.b[3] = 20;
+    dstport.NotAnInteger = swap16((mDNSu16)49339);
+    
+    // Set message to a DNS message (copied from a WireShark packet)
+    msg = (DNSMessage *)udns_query_request_message;
+    end = udns_query_request_message + sizeof(udns_query_request_message);
+    
+    // Execute mDNSCoreReceive using a valid DNS message
+    mDNSCoreReceive(m, msg, end, &srcaddr, srcport, &dstaddr, dstport, if_nametoindex("en0"));
+    
+    // Verify that mDNSCoreReceiveQuery traversed the normal code path
+    XCTAssertEqual(m->mDNSStats.NormalQueries,  1);
+}
+
+- (void)testNullDstQueryReqTest
+{
+    mDNS *const m = &mDNSStorage;
+    mDNSAddr srcaddr;
+    mDNSIPPort srcport, dstport;
+    DNSMessage * msg;
+    const mDNSu8 * end;
+
+    // This test case does not require setup of interfaces, the record's cache, or pending questions
+    // so m is initialized to all zeros.
+    InitmDNSStorage(m);
+
+    // Used random values for srcaddr and srcport
+    srcaddr.type       = mDNSAddrType_IPv4;
+    srcaddr.ip.v4.b[0] = 192;
+    srcaddr.ip.v4.b[1] = 168;
+    srcaddr.ip.v4.b[2] = 1;
+    srcaddr.ip.v4.b[3] = 10;
+    srcport.NotAnInteger = swap16((mDNSu16)53);
+
+    // Used random value for dstport
+    dstport.NotAnInteger = swap16((mDNSu16)49339);
+
+    // Set message to a DNS message (copied from a WireShark packet)
+    msg = (DNSMessage *)udns_query_request_message_with_invalid_id;
+    end = udns_query_request_message_with_invalid_id + sizeof(udns_query_request_message_with_invalid_id);
+
+    // Execute mDNSCoreReceive to regress <rdar://problem/28556513>
+    mDNSCoreReceive(m, msg, end, &srcaddr, srcport, NULL, dstport, if_nametoindex("en0"));
+
+    // Verify that mDNSCoreReceiveQuery was NOT traversed through the normal code path
+    XCTAssertEqual(m->mDNSStats.NormalQueries,  0);
+
+    // Verify code path that previously crashed, in <rdar://problem/28556513>, now traverses successfully
+    // by checking a counter that was incremented on code path that crashed.
+    XCTAssertEqual(m->MPktNum,                  1);
+}
+
+#if 0
+- (void)testPerformanceExample {
+    // This is an example of a performance test case.
+    [self measureBlock:^{
+        // Put the code you want to measure the time of here.
+    }];
+}
+#endif
+
+@end
diff --git a/mDNSMacOSX/Tests/mDNSCoreReceiveTest.m b/mDNSMacOSX/Tests/mDNSCoreReceiveTest.m
deleted file mode 100644 (file)
index e2c3245..0000000
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (c) 2017-2018 Apple Inc. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "unittest_common.h"
-#import <XCTest/XCTest.h>
-
-// This DNS message was gleaned from a uDNS query request packet that was captured with Wireshark.
-uint8_t udns_query_request_message[28] = {            // contains 1 question for www.f5.com
-    0x31, 0xca, // transaction id
-    0x01, 0x00,    // flags
-    0x00, 0x01,    // 1 question
-    0x00, 0x00,    // no anwsers
-    0x00, 0x00,    // no authoritative answers
-    0x00, 0x00, // no additionals
-    0x03, 0x77, 0x77, 0x77, 0x02, 0x66, 0x35, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01
-};
-
-// This DNS message was gleaned from a uDNS query request packet that was captured with Wireshark.
-// Then the header id (more specifically, the msg->h.id) was deliberately cleared to force code
-// path to traverse regression case, <rdar://problem/28556513>.
-uint8_t udns_query_request_message_with_invalid_id[28] = {  // contains 1 question for www.f5.com, msg->h.id = 0
-    0x00, 0x00,    // transaction id
-    0x01, 0x00, // flags
-    0x00, 0x01, // 1 question
-    0x00, 0x00, // no anwsers
-    0x00, 0x00, // no authoritative answers
-    0x00, 0x00, // no additionals
-    0x03, 0x77, 0x77, 0x77, 0x02, 0x66, 0x35, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01
-};
-
-uint8_t arp_request_packet[28] = {  // contains 1 question for www.f5.com, msg->h.id = 0
-    0x00, 0x01, // hardware type: enet
-    0x08, 0x00, // protocol type: IP
-    0x06, // hardware size
-    0x04, // Protcol size
-    0x00, 0x01, // opcode request
-    0x24, 0x01, 0xc7, 0x24, 0x35, 0x00, // Sender mac addr
-    0x11, 0xe2, 0x14, 0x01,    // Sender ip addr
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,    // target mac addr
-    0x11, 0xe2, 0x17, 0xbe    // target ip addr
-};
-
-mDNSlocal void InitmDNSStorage(mDNS *const m)
-{
-    memset(m, 0, sizeof(mDNS));
-}
-
-@interface mDNSCoreReceiveTest : XCTestCase
-{
-}
-@end
-
-@implementation mDNSCoreReceiveTest
-
-- (void)setUp
-{
-    mDNSPlatformTimeInit();
-    mDNS_LoggingEnabled = 0;
-    mDNS_PacketLoggingEnabled = 0;
-}
-
-- (void)tearDown {
-}
-
-- (void)testValidQueryReq
-{
-    mDNS *const m = &mDNSStorage;
-    mDNSAddr srcaddr, dstaddr;
-    mDNSIPPort srcport, dstport;
-    DNSMessage * msg;
-    const mDNSu8 * end;
-    
-    // Init unit test environment and verify no error occurred.
-    mStatus result = init_mdns_environment(mDNStrue);
-    XCTAssertEqual(result, mStatus_NoError);
-    
-    // Used random values for srcaddr and srcport
-    srcaddr.type       = mDNSAddrType_IPv4;
-    srcaddr.ip.v4.b[0] = 192;
-    srcaddr.ip.v4.b[1] = 168;
-    srcaddr.ip.v4.b[2] = 1;
-    srcaddr.ip.v4.b[3] = 10;
-    srcport.NotAnInteger = swap16((mDNSu16)53);
-    
-    // Used random values for dstaddr and dstport
-    dstaddr.type       = mDNSAddrType_IPv4;
-    dstaddr.ip.v4.b[0] = 192;
-    dstaddr.ip.v4.b[1] = 168;
-    dstaddr.ip.v4.b[2] = 1;
-    dstaddr.ip.v4.b[3] = 20;
-    dstport.NotAnInteger = swap16((mDNSu16)49339);
-    
-    // Set message to a DNS message (copied from a WireShark packet)
-    msg = (DNSMessage *)udns_query_request_message;
-    end = udns_query_request_message + sizeof(udns_query_request_message);
-    
-    // Execute mDNSCoreReceive using a valid DNS message
-    mDNSCoreReceive(m, msg, end, &srcaddr, srcport, &dstaddr, dstport, if_nametoindex("en0"));
-    
-    // Verify that mDNSCoreReceiveQuery traversed the normal code path
-    XCTAssertEqual(m->mDNSStats.NormalQueries,  1);
-}
-
-- (void)testNullDstQueryReqTest
-{
-    mDNS *const m = &mDNSStorage;
-    mDNSAddr srcaddr;
-    mDNSIPPort srcport, dstport;
-    DNSMessage * msg;
-    const mDNSu8 * end;
-
-    // This test case does not require setup of interfaces, the record's cache, or pending questions
-    // so m is initialized to all zeros.
-    InitmDNSStorage(m);
-
-    // Used random values for srcaddr and srcport
-    srcaddr.type       = mDNSAddrType_IPv4;
-    srcaddr.ip.v4.b[0] = 192;
-    srcaddr.ip.v4.b[1] = 168;
-    srcaddr.ip.v4.b[2] = 1;
-    srcaddr.ip.v4.b[3] = 10;
-    srcport.NotAnInteger = swap16((mDNSu16)53);
-
-    // Used random value for dstport
-    dstport.NotAnInteger = swap16((mDNSu16)49339);
-
-    // Set message to a DNS message (copied from a WireShark packet)
-    msg = (DNSMessage *)udns_query_request_message_with_invalid_id;
-    end = udns_query_request_message_with_invalid_id + sizeof(udns_query_request_message_with_invalid_id);
-
-    // Execute mDNSCoreReceive to regress <rdar://problem/28556513>
-    mDNSCoreReceive(m, msg, end, &srcaddr, srcport, NULL, dstport, if_nametoindex("en0"));
-
-    // Verify that mDNSCoreReceiveQuery was NOT traversed through the normal code path
-    XCTAssertEqual(m->mDNSStats.NormalQueries,  0);
-
-    // Verify code path that previously crashed, in <rdar://problem/28556513>, now traverses successfully
-    // by checking a counter that was incremented on code path that crashed.
-    XCTAssertEqual(m->MPktNum,                  1);
-}
-
-
-#if 0
-- (void)testPerformanceExample {
-    // This is an example of a performance test case.
-    [self measureBlock:^{
-        // Put the code you want to measure the time of here.
-    }];
-}
-#endif
-
-@end
index 025d2128752e0e90d3e487edf990eefdefc7b7ed..7c6d64854b8ebd5b93ccec9febc87e604ea4734d 100644 (file)
        </dict>
        <key>Tests</key>
        <array>
+               <dict>
+                       <key>TestName</key>
+                       <string>mDNSResponder Leaks</string>
+                       <key>Description</key>
+                       <string>Checks mDNSResponder for memory leaks.</string>
+                       <key>AsRoot</key>
+                       <true/>
+                       <key>RequiresWiFi</key>
+                       <false/>
+                       <key>Timeout</key>
+                       <integer>10</integer>
+                       <key>IgnoreOutput</key>
+                       <true/>
+                       <key>Command</key>
+                       <array>
+                               <string>/usr/bin/leaks</string>
+                               <string>mDNSResponder</string>
+                       </array>
+               </dict>
                <dict>
                        <key>TestName</key>
                        <string>GAIPerf Advanced</string>
                                <string>--skipPathEval</string>
                        </array>
                </dict>
+               <dict>
+                       <key>TestName</key>
+                       <string>mDNSResponder Leaks</string>
+                       <key>Description</key>
+                       <string>Checks mDNSResponder for memory leaks.</string>
+                       <key>AsRoot</key>
+                       <true/>
+                       <key>RequiresWiFi</key>
+                       <false/>
+                       <key>Timeout</key>
+                       <integer>10</integer>
+                       <key>IgnoreOutput</key>
+                       <true/>
+                       <key>Command</key>
+                       <array>
+                               <string>/usr/bin/leaks</string>
+                               <string>mDNSResponder</string>
+                       </array>
+               </dict>
                <dict>
                        <key>TestName</key>
                        <string>mDNS Discovery 1-1-1</string>
                                <string>--flushCache</string>
                        </array>
                </dict>
+               <dict>
+                       <key>TestName</key>
+                       <string>mDNSResponder Leaks</string>
+                       <key>Description</key>
+                       <string>Checks mDNSResponder for memory leaks.</string>
+                       <key>AsRoot</key>
+                       <true/>
+                       <key>RequiresWiFi</key>
+                       <false/>
+                       <key>Timeout</key>
+                       <integer>10</integer>
+                       <key>IgnoreOutput</key>
+                       <true/>
+                       <key>Command</key>
+                       <array>
+                               <string>/usr/bin/leaks</string>
+                               <string>mDNSResponder</string>
+                       </array>
+               </dict>
                <dict>
                        <key>TestName</key>
                        <string>mDNS Discovery 1-1-1 (No Additionals)</string>
                                <string>--flushCache</string>
                        </array>
                </dict>
+               <dict>
+                       <key>TestName</key>
+                       <string>mDNSResponder Leaks</string>
+                       <key>Description</key>
+                       <string>Checks mDNSResponder for memory leaks.</string>
+                       <key>AsRoot</key>
+                       <true/>
+                       <key>RequiresWiFi</key>
+                       <false/>
+                       <key>Timeout</key>
+                       <integer>10</integer>
+                       <key>IgnoreOutput</key>
+                       <true/>
+                       <key>Command</key>
+                       <array>
+                               <string>/usr/bin/leaks</string>
+                               <string>mDNSResponder</string>
+                       </array>
+               </dict>
                <dict>
                        <key>TestName</key>
                        <string>mDNS Discovery 10-100-2</string>
                                <string>--flushCache</string>
                        </array>
                </dict>
+               <dict>
+                       <key>TestName</key>
+                       <string>mDNSResponder Leaks</string>
+                       <key>Description</key>
+                       <string>Checks mDNSResponder for memory leaks.</string>
+                       <key>AsRoot</key>
+                       <true/>
+                       <key>RequiresWiFi</key>
+                       <false/>
+                       <key>Timeout</key>
+                       <integer>10</integer>
+                       <key>IgnoreOutput</key>
+                       <true/>
+                       <key>Command</key>
+                       <array>
+                               <string>/usr/bin/leaks</string>
+                               <string>mDNSResponder</string>
+                       </array>
+               </dict>
                <dict>
                        <key>TestName</key>
                        <string>mDNS Discovery 10-100-2 (No Additionals)</string>
                                <string>--flushCache</string>
                        </array>
                </dict>
+               <dict>
+                       <key>TestName</key>
+                       <string>mDNSResponder Leaks</string>
+                       <key>Description</key>
+                       <string>Checks mDNSResponder for memory leaks.</string>
+                       <key>AsRoot</key>
+                       <true/>
+                       <key>RequiresWiFi</key>
+                       <false/>
+                       <key>Timeout</key>
+                       <integer>10</integer>
+                       <key>IgnoreOutput</key>
+                       <true/>
+                       <key>Command</key>
+                       <array>
+                               <string>/usr/bin/leaks</string>
+                               <string>mDNSResponder</string>
+                       </array>
+               </dict>
                <dict>
                        <key>TestName</key>
                        <string>mDNS Discovery 100-500-2</string>
                                <string>--flushCache</string>
                        </array>
                </dict>
+               <dict>
+                       <key>TestName</key>
+                       <string>mDNSResponder Leaks</string>
+                       <key>Description</key>
+                       <string>Checks mDNSResponder for memory leaks.</string>
+                       <key>AsRoot</key>
+                       <true/>
+                       <key>RequiresWiFi</key>
+                       <false/>
+                       <key>Timeout</key>
+                       <integer>10</integer>
+                       <key>IgnoreOutput</key>
+                       <true/>
+                       <key>Command</key>
+                       <array>
+                               <string>/usr/bin/leaks</string>
+                               <string>mDNSResponder</string>
+                       </array>
+               </dict>
                <dict>
                        <key>TestName</key>
                        <string>mDNS Discovery 100-500-2 (No Additionals)</string>
                                <string>--flushCache</string>
                        </array>
                </dict>
+               <dict>
+                       <key>TestName</key>
+                       <string>mDNSResponder Leaks</string>
+                       <key>Description</key>
+                       <string>Checks mDNSResponder for memory leaks.</string>
+                       <key>AsRoot</key>
+                       <true/>
+                       <key>RequiresWiFi</key>
+                       <false/>
+                       <key>Timeout</key>
+                       <integer>10</integer>
+                       <key>IgnoreOutput</key>
+                       <true/>
+                       <key>Command</key>
+                       <array>
+                               <string>/usr/bin/leaks</string>
+                               <string>mDNSResponder</string>
+                       </array>
+               </dict>
                <dict>
                        <key>TestName</key>
                        <string>mDNS Discovery 1-1-1 (No Cache Flush)</string>
                                <string>json</string>
                        </array>
                </dict>
+               <dict>
+                       <key>TestName</key>
+                       <string>mDNSResponder Leaks</string>
+                       <key>Description</key>
+                       <string>Checks mDNSResponder for memory leaks.</string>
+                       <key>AsRoot</key>
+                       <true/>
+                       <key>RequiresWiFi</key>
+                       <false/>
+                       <key>Timeout</key>
+                       <integer>10</integer>
+                       <key>IgnoreOutput</key>
+                       <true/>
+                       <key>Command</key>
+                       <array>
+                               <string>/usr/bin/leaks</string>
+                               <string>mDNSResponder</string>
+                       </array>
+               </dict>
                <dict>
                        <key>TestName</key>
                        <string>mDNS Discovery 1-1-1 (No Cache Flush, No Additionals)</string>
                                <string>--noAdditionals</string>
                        </array>
                </dict>
+               <dict>
+                       <key>TestName</key>
+                       <string>mDNSResponder Leaks</string>
+                       <key>Description</key>
+                       <string>Checks mDNSResponder for memory leaks.</string>
+                       <key>AsRoot</key>
+                       <true/>
+                       <key>RequiresWiFi</key>
+                       <false/>
+                       <key>Timeout</key>
+                       <integer>10</integer>
+                       <key>IgnoreOutput</key>
+                       <true/>
+                       <key>Command</key>
+                       <array>
+                               <string>/usr/bin/leaks</string>
+                               <string>mDNSResponder</string>
+                       </array>
+               </dict>
                <dict>
                        <key>TestName</key>
                        <string>mDNS Discovery 10-100-2 (No Cache Flush)</string>
                                <string>json</string>
                        </array>
                </dict>
+               <dict>
+                       <key>TestName</key>
+                       <string>mDNSResponder Leaks</string>
+                       <key>Description</key>
+                       <string>Checks mDNSResponder for memory leaks.</string>
+                       <key>AsRoot</key>
+                       <true/>
+                       <key>RequiresWiFi</key>
+                       <false/>
+                       <key>Timeout</key>
+                       <integer>10</integer>
+                       <key>IgnoreOutput</key>
+                       <true/>
+                       <key>Command</key>
+                       <array>
+                               <string>/usr/bin/leaks</string>
+                               <string>mDNSResponder</string>
+                       </array>
+               </dict>
                <dict>
                        <key>TestName</key>
                        <string>mDNS Discovery 10-100-2 (No Cache Flush, No Additionals)</string>
                                <string>--noAdditionals</string>
                        </array>
                </dict>
+               <dict>
+                       <key>TestName</key>
+                       <string>mDNSResponder Leaks</string>
+                       <key>Description</key>
+                       <string>Checks mDNSResponder for memory leaks.</string>
+                       <key>AsRoot</key>
+                       <true/>
+                       <key>RequiresWiFi</key>
+                       <false/>
+                       <key>Timeout</key>
+                       <integer>10</integer>
+                       <key>IgnoreOutput</key>
+                       <true/>
+                       <key>Command</key>
+                       <array>
+                               <string>/usr/bin/leaks</string>
+                               <string>mDNSResponder</string>
+                       </array>
+               </dict>
                <dict>
                        <key>TestName</key>
                        <string>mDNS Discovery 100-500-2 (No Cache Flush)</string>
                                <string>json</string>
                        </array>
                </dict>
+               <dict>
+                       <key>TestName</key>
+                       <string>mDNSResponder Leaks</string>
+                       <key>Description</key>
+                       <string>Checks mDNSResponder for memory leaks.</string>
+                       <key>AsRoot</key>
+                       <true/>
+                       <key>RequiresWiFi</key>
+                       <false/>
+                       <key>Timeout</key>
+                       <integer>10</integer>
+                       <key>IgnoreOutput</key>
+                       <true/>
+                       <key>Command</key>
+                       <array>
+                               <string>/usr/bin/leaks</string>
+                               <string>mDNSResponder</string>
+                       </array>
+               </dict>
                <dict>
                        <key>TestName</key>
                        <string>mDNS Discovery 100-500-2 (No Cache Flush, No Additionals)</string>
                                <string>json</string>
                        </array>
                </dict>
+               <dict>
+                       <key>TestName</key>
+                       <string>mDNSResponder Leaks</string>
+                       <key>Description</key>
+                       <string>Checks mDNSResponder for memory leaks.</string>
+                       <key>AsRoot</key>
+                       <true/>
+                       <key>RequiresWiFi</key>
+                       <false/>
+                       <key>Timeout</key>
+                       <integer>10</integer>
+                       <key>IgnoreOutput</key>
+                       <true/>
+                       <key>Command</key>
+                       <array>
+                               <string>/usr/bin/leaks</string>
+                               <string>mDNSResponder</string>
+                       </array>
+               </dict>
                <dict>
                        <key>TestName</key>
                        <string>mDNS Discovery w/Packet Drops 10</string>
                                <string>--flushCache</string>
                        </array>
                </dict>
+               <dict>
+                       <key>TestName</key>
+                       <string>mDNSResponder Leaks</string>
+                       <key>Description</key>
+                       <string>Checks mDNSResponder for memory leaks.</string>
+                       <key>AsRoot</key>
+                       <true/>
+                       <key>RequiresWiFi</key>
+                       <false/>
+                       <key>Timeout</key>
+                       <integer>10</integer>
+                       <key>IgnoreOutput</key>
+                       <true/>
+                       <key>Command</key>
+                       <array>
+                               <string>/usr/bin/leaks</string>
+                               <string>mDNSResponder</string>
+                       </array>
+               </dict>
                <dict>
                        <key>TestName</key>
                        <string>mDNS Discovery w/Packet Drops 100</string>
                                <string>--flushCache</string>
                        </array>
                </dict>
+               <dict>
+                       <key>TestName</key>
+                       <string>mDNSResponder Leaks</string>
+                       <key>Description</key>
+                       <string>Checks mDNSResponder for memory leaks.</string>
+                       <key>AsRoot</key>
+                       <true/>
+                       <key>RequiresWiFi</key>
+                       <false/>
+                       <key>Timeout</key>
+                       <integer>10</integer>
+                       <key>IgnoreOutput</key>
+                       <true/>
+                       <key>Command</key>
+                       <array>
+                               <string>/usr/bin/leaks</string>
+                               <string>mDNSResponder</string>
+                       </array>
+               </dict>
                <dict>
                        <key>TestName</key>
                        <string>DotLocal Queries</string>
                                <string>json</string>
                        </array>
                </dict>
+               <dict>
+                       <key>TestName</key>
+                       <string>mDNSResponder Leaks</string>
+                       <key>Description</key>
+                       <string>Checks mDNSResponder for memory leaks.</string>
+                       <key>AsRoot</key>
+                       <true/>
+                       <key>RequiresWiFi</key>
+                       <false/>
+                       <key>Timeout</key>
+                       <integer>10</integer>
+                       <key>IgnoreOutput</key>
+                       <true/>
+                       <key>Command</key>
+                       <array>
+                               <string>/usr/bin/leaks</string>
+                               <string>mDNSResponder</string>
+                       </array>
+               </dict>
                <dict>
                        <key>TestName</key>
                        <string>Service Registration</string>
                                <string>--bats</string>
                        </array>
                </dict>
+               <dict>
+                       <key>TestName</key>
+                       <string>mDNSResponder Leaks</string>
+                       <key>Description</key>
+                       <string>Checks mDNSResponder for memory leaks.</string>
+                       <key>AsRoot</key>
+                       <true/>
+                       <key>RequiresWiFi</key>
+                       <false/>
+                       <key>Timeout</key>
+                       <integer>10</integer>
+                       <key>IgnoreOutput</key>
+                       <true/>
+                       <key>Command</key>
+                       <array>
+                               <string>/usr/bin/leaks</string>
+                               <string>mDNSResponder</string>
+                       </array>
+               </dict>
                <dict>
                        <key>TestName</key>
                        <string>Probe Conflicts</string>
                                <string>json</string>
                        </array>
                </dict>
+               <dict>
+                       <key>TestName</key>
+                       <string>mDNSResponder Leaks</string>
+                       <key>Description</key>
+                       <string>Checks mDNSResponder for memory leaks.</string>
+                       <key>AsRoot</key>
+                       <true/>
+                       <key>RequiresWiFi</key>
+                       <false/>
+                       <key>Timeout</key>
+                       <integer>10</integer>
+                       <key>IgnoreOutput</key>
+                       <true/>
+                       <key>Command</key>
+                       <array>
+                               <string>/usr/bin/leaks</string>
+                               <string>mDNSResponder</string>
+                       </array>
+               </dict>
                <dict>
                        <key>TestName</key>
                        <string>TCP Fallback</string>
                                <string>--badUDPMode</string>
                        </array>
                </dict>
+               <dict>
+                       <key>TestName</key>
+                       <string>mDNSResponder Leaks</string>
+                       <key>Description</key>
+                       <string>Checks mDNSResponder for memory leaks.</string>
+                       <key>AsRoot</key>
+                       <true/>
+                       <key>RequiresWiFi</key>
+                       <false/>
+                       <key>Timeout</key>
+                       <integer>10</integer>
+                       <key>IgnoreOutput</key>
+                       <true/>
+                       <key>Command</key>
+                       <array>
+                               <string>/usr/bin/leaks</string>
+                               <string>mDNSResponder</string>
+                       </array>
+               </dict>
                <dict>
                        <key>TestName</key>
                        <string>State Dump</string>
                                <string>/AppleInternal/Tests/mDNSResponder/bats_test_state_dump.sh</string>
                        </array>
                </dict>
+               <dict>
+                       <key>TestName</key>
+                       <string>mDNSResponder Leaks</string>
+                       <key>Description</key>
+                       <string>Checks mDNSResponder for memory leaks.</string>
+                       <key>AsRoot</key>
+                       <true/>
+                       <key>RequiresWiFi</key>
+                       <false/>
+                       <key>Timeout</key>
+                       <integer>10</integer>
+                       <key>IgnoreOutput</key>
+                       <true/>
+                       <key>Command</key>
+                       <array>
+                               <string>/usr/bin/leaks</string>
+                               <string>mDNSResponder</string>
+                       </array>
+               </dict>
                <dict>
                        <key>TestName</key>
                        <string>DNS Proxy</string>
                                <string>/AppleInternal/Tests/mDNSResponder/bats_test_proxy.sh</string>
                        </array>
                </dict>
+               <dict>
+                       <key>TestName</key>
+                       <string>mDNSResponder Leaks</string>
+                       <key>Description</key>
+                       <string>Checks mDNSResponder for memory leaks.</string>
+                       <key>AsRoot</key>
+                       <true/>
+                       <key>RequiresWiFi</key>
+                       <false/>
+                       <key>Timeout</key>
+                       <integer>10</integer>
+                       <key>IgnoreOutput</key>
+                       <true/>
+                       <key>Command</key>
+                       <array>
+                               <string>/usr/bin/leaks</string>
+                               <string>mDNSResponder</string>
+                       </array>
+               </dict>
                <dict>
                        <key>TestName</key>
                        <string>Expensive/Constrained Interface</string>
                                <string>Tests.xctest</string>
                        </array>
                </dict>
+               <dict>
+                       <key>TestName</key>
+                       <string>Fix Verification #1</string>
+                       <key>Description</key>
+                       <string>Fix Verification #1</string>
+                       <key>AsRoot</key>
+                       <true/>
+                       <key>RequiresWiFi</key>
+                       <false/>
+                       <key>Timeout</key>
+                       <integer>45</integer>
+                       <key>IgnoreOutput</key>
+                       <true/>
+                       <key>Command</key>
+                       <array>
+                               <string>/usr/local/bin/dnssdutil</string>
+                               <string>verifyFix</string>
+                               <string>earlyAWDL</string>
+                               <string>--format</string>
+                               <string>json</string>
+                       </array>
+               </dict>
+               <dict>
+                       <key>TestName</key>
+                       <string>mDNSResponder Leaks</string>
+                       <key>Description</key>
+                       <string>Checks mDNSResponder for memory leaks.</string>
+                       <key>AsRoot</key>
+                       <true/>
+                       <key>RequiresWiFi</key>
+                       <false/>
+                       <key>Timeout</key>
+                       <integer>10</integer>
+                       <key>IgnoreOutput</key>
+                       <true/>
+                       <key>Command</key>
+                       <array>
+                               <string>/usr/bin/leaks</string>
+                               <string>mDNSResponder</string>
+                       </array>
+               </dict>
        </array>
 </dict>
 </plist>
index 2c7ee9519bda7e81cf1dac578d2690c5f0008202..3236966988d0a54bb0f6db426aea01a8bcfe76a9 100644 (file)
@@ -5477,9 +5477,9 @@ mDNSlocal void SetLocalDomains(void)
     CFRelease(sa);
 }
 
+#if !MDNSRESPONDER_SUPPORTS(APPLE, NO_WAKE_FOR_NET_ACCESS)
 mDNSlocal void GetCurrentPMSetting(const CFStringRef name, mDNSs32 *val)
 {
-    
     CFDictionaryRef dict = SCDynamicStoreCopyValue(NULL, NetworkChangedKey_PowerSettings);
     if (!dict)
     {
@@ -5492,8 +5492,8 @@ mDNSlocal void GetCurrentPMSetting(const CFStringRef name, mDNSs32 *val)
             *val = 0;
         CFRelease(dict);
     }
-    
 }
+#endif
 
 #if APPLE_OSX_mDNSResponder
 
@@ -5998,14 +5998,13 @@ mDNSexport mStatus ActivateLocalProxy(NetworkInterfaceInfo *const intf, mDNSBool
 
 mDNSlocal mDNSu8 SystemWakeForNetworkAccess(void)
 {
+#if MDNSRESPONDER_SUPPORTS(APPLE, NO_WAKE_FOR_NET_ACCESS)
+    LogRedact(MDNS_LOG_CATEGORY_SPS, MDNS_LOG_DEBUG, "SystemWakeForNetworkAccess: compile-time disabled");
+    return ((mDNSu8)mDNS_NoWake);
+#else
     mDNSs32 val = 0;
     mDNSu8  ret = (mDNSu8)mDNS_NoWake;
 
-#if TARGET_OS_IOS
-    LogSPS("SystemWakeForNetworkAccess: Sleep Proxy Client always disabled on TARGET_OS_IOS");
-    return ret;
-#endif
-
     if (DisableSleepProxyClient)
     {
        LogSPS("SystemWakeForNetworkAccess: Sleep Proxy Client disabled by command-line option");
@@ -6025,6 +6024,7 @@ mDNSlocal mDNSu8 SystemWakeForNetworkAccess(void)
 
     LogSPS("SystemWakeForNetworkAccess: Wake On LAN: %d", ret);
     return ret;
+#endif
 }
 
 mDNSlocal mDNSBool SystemSleepOnlyIfWakeOnLAN(void)
index b89ec2c82f0d42dde7ec51d140a5729d8ab0000e..8423ab63e8df8d6afd125386ec67ca1a34735a03 100644 (file)
                B7473E981EC3C78300D31B9D /* _CNDomainBrowser.m in Sources */ = {isa = PBXBuildFile; fileRef = B7D6CA5E1D107573005E24CF /* _CNDomainBrowser.m */; };
                B7473E991EC3C86600D31B9D /* ClientCommon.c in Sources */ = {isa = PBXBuildFile; fileRef = FF5852100DD27BD300862BDF /* ClientCommon.c */; };
                B74A96261DD4EDE60084A8C5 /* Preferences.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B74A96251DD4EDE60084A8C5 /* Preferences.framework */; };
+               B74BF92E2322E97400E35354 /* SuspiciousReplyTest.m in Sources */ = {isa = PBXBuildFile; fileRef = B74BF92D2322E97400E35354 /* SuspiciousReplyTest.m */; };
+               B74BF931232701F600E35354 /* CacheOrderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = B74BF930232701F600E35354 /* CacheOrderTest.m */; };
                B74F16F4211BA55400BEBE84 /* DNSMessageTest.m in Sources */ = {isa = PBXBuildFile; fileRef = B74F16F3211BA55400BEBE84 /* DNSMessageTest.m */; };
                B74F2B461E82FEAE0084960E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BDA3F0891C48DB910054FB4B /* Foundation.framework */; };
                B74F2B471E82FEFE0084960E /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F869685066EE02400D2A2DC /* Security.framework */; };
                B7D6CA7A1D107714005E24CF /* CNDomainBrowserView.m in Sources */ = {isa = PBXBuildFile; fileRef = B7D6CA691D1076C6005E24CF /* CNDomainBrowserView.m */; };
                B7E06B0D1DBA9DFE00E4580C /* ClientCommon.c in Sources */ = {isa = PBXBuildFile; fileRef = FF5852100DD27BD300862BDF /* ClientCommon.c */; };
                B7E06B0E1DBA9E9700E4580C /* DomainBrowser.strings in Resources */ = {isa = PBXBuildFile; fileRef = B7016F4F1D5D0D1900107E7C /* DomainBrowser.strings */; };
+               B7E06CE12329B0480021401F /* LocalOnlyWithInterfacesTest.m in Sources */ = {isa = PBXBuildFile; fileRef = B7E06CDF2329AA7B0021401F /* LocalOnlyWithInterfacesTest.m */; };
                B7EEF7C1212601460093828F /* mDNSMacOSX.c in Sources */ = {isa = PBXBuildFile; fileRef = 6575FBEB022EAF7200000109 /* mDNSMacOSX.c */; };
                B7EEF7C2212602EC0093828F /* mDNS.c in Sources */ = {isa = PBXBuildFile; fileRef = 6575FBE9022EAF5A00000109 /* mDNS.c */; };
                B7EEF7C4212603B20093828F /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BD893CE4206C0D980055F9E7 /* SystemConfiguration.framework */; };
                B7473E8A1EC395C300D31B9D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
                B7473E8B1EC395C300D31B9D /* script.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = script.js; sourceTree = "<group>"; };
                B74A96251DD4EDE60084A8C5 /* Preferences.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Preferences.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.0.Internal.sdk/System/Library/PrivateFrameworks/Preferences.framework; sourceTree = DEVELOPER_DIR; };
+               B74BF92D2322E97400E35354 /* SuspiciousReplyTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SuspiciousReplyTest.m; sourceTree = "<group>"; };
+               B74BF930232701F600E35354 /* CacheOrderTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CacheOrderTest.m; sourceTree = "<group>"; };
                B74EC1161D47FC7700A1D155 /* BonjourSettings.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BonjourSettings.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
                B74EC11B1D47FC7800A1D155 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = ../SettingsBundle/Info.plist; sourceTree = "<group>"; };
                B74EC1271D494C5800A1D155 /* DomainBrowser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DomainBrowser.h; sourceTree = "<group>"; };
                B7D6CA681D1076C6005E24CF /* CNDomainBrowserView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CNDomainBrowserView.h; sourceTree = "<group>"; };
                B7D6CA691D1076C6005E24CF /* CNDomainBrowserView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CNDomainBrowserView.m; sourceTree = "<group>"; };
                B7D6CA701D1076F3005E24CF /* DomainBrowser.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DomainBrowser.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+               B7E06CDF2329AA7B0021401F /* LocalOnlyWithInterfacesTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LocalOnlyWithInterfacesTest.m; sourceTree = "<group>"; };
                BD03E88C1AD31278005E8A81 /* SymptomReporter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SymptomReporter.c; sourceTree = "<group>"; };
                BD11266D21DB1AFE006115E6 /* dnssd_xpc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dnssd_xpc.h; sourceTree = "<group>"; };
                BD11266E21DB1AFE006115E6 /* dnssd_xpc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dnssd_xpc.c; sourceTree = "<group>"; };
                        name = "Supporting Files";
                        sourceTree = "<group>";
                };
+               B74BF92F2322E99700E35354 /* Unit Tests */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B74F16EF2114E49D00BEBE84 /* Info.plist */,
+                               B7A86198212B074500E81CC3 /* LocalOnlyTimeoutTest.m */,
+                               B7A861962127845700E81CC3 /* CNameRecordTest.m */,
+                               B7A8618A21274FA200E81CC3 /* mDNSCoreReceiveTest.m */,
+                               B7A8618821274BFC00E81CC3 /* ResourceRecordTest.m */,
+                               B74F16F3211BA55400BEBE84 /* DNSMessageTest.m */,
+                               D459F0D3222862BA0056AC5B /* HelperFunctionTest.m */,
+                               B74BF92D2322E97400E35354 /* SuspiciousReplyTest.m */,
+                               B74BF930232701F600E35354 /* CacheOrderTest.m */,
+                               B7E06CDF2329AA7B0021401F /* LocalOnlyWithInterfacesTest.m */,
+                       );
+                       path = "Unit Tests";
+                       sourceTree = "<group>";
+               };
                B74EC11A1D47FC7800A1D155 /* SettingsBundle */ = {
                        isa = PBXGroup;
                        children = (
                B74F16EC2114E49D00BEBE84 /* Tests */ = {
                        isa = PBXGroup;
                        children = (
-                               B74F16EF2114E49D00BEBE84 /* Info.plist */,
                                BD98A796213A3EAE0002EC47 /* mDNSResponder.plist */,
-                               B7A86198212B074500E81CC3 /* LocalOnlyTimeoutTest.m */,
-                               B7A861962127845700E81CC3 /* CNameRecordTest.m */,
-                               B7A8618A21274FA200E81CC3 /* mDNSCoreReceiveTest.m */,
-                               B7A8618821274BFC00E81CC3 /* ResourceRecordTest.m */,
-                               B74F16F3211BA55400BEBE84 /* DNSMessageTest.m */,
-                               D459F0D3222862BA0056AC5B /* HelperFunctionTest.m */,
                                D448F7A6222DA98E0069E1D2 /* BATS Scripts */,
                                37DDE9241BA382280092AC61 /* Unit Test Utilities */,
+                               B74BF92F2322E99700E35354 /* Unit Tests */,
                        );
                        path = Tests;
                        sourceTree = "<group>";
                        files = (
                                898E98392203633800812DC6 /* dnssd_clientshim.c in Sources */,
                                D459F0D4222862BA0056AC5B /* HelperFunctionTest.m in Sources */,
+                               B74BF931232701F600E35354 /* CacheOrderTest.m in Sources */,
                                898E983A2203633800812DC6 /* dso-transport.c in Sources */,
+                               B7E06CE12329B0480021401F /* LocalOnlyWithInterfacesTest.m in Sources */,
                                898E983B2203633800812DC6 /* dso.c in Sources */,
                                B7EEF7C1212601460093828F /* mDNSMacOSX.c in Sources */,
                                B7EEF7CC212604A80093828F /* LegacyNATTraversal.c in Sources */,
                                B7EEF7D6212606F50093828F /* uDNSPathEvalulation.c in Sources */,
                                B7EEF7EA212613260093828F /* uds_daemon.c in Sources */,
                                B7A861952127806600E81CC3 /* unittest_common.c in Sources */,
+                               B74BF92E2322E97400E35354 /* SuspiciousReplyTest.m in Sources */,
                                B7EEF7E921260E4C0093828F /* CryptoSupport.c in Sources */,
                                B7EEF7D9212607C40093828F /* nsec.c in Sources */,
                                D40123912272B6E3006C9BBE /* mdns_object.m in Sources */,
                                CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVE = YES;
                                CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+                               CODE_SIGN_IDENTITY = "-";
                                COMBINE_HIDPI_IMAGES = YES;
                                DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
                                ENABLE_NS_ASSERTIONS = NO;
                                        "$(SDKROOT)/usr/include/libxml2",
                                        "${CONFIGURATION_TEMP_DIR}",
                                );
-                               INFOPLIST_FILE = Tests/Info.plist;
+                               INFOPLIST_FILE = "Tests/Unit Tests/Info.plist";
                                INSTALL_PATH = /AppleInternal/XCTests/com.apple.mDNSResponder/;
                                LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
                                MTL_FAST_MATH = YES;
                                CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVE = YES;
                                CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+                               CODE_SIGN_IDENTITY = "-";
                                COMBINE_HIDPI_IMAGES = YES;
                                DEBUG_INFORMATION_FORMAT = dwarf;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                        "$(SDKROOT)/usr/include/libxml2",
                                        "${CONFIGURATION_TEMP_DIR}",
                                );
-                               INFOPLIST_FILE = Tests/Info.plist;
+                               INFOPLIST_FILE = "Tests/Unit Tests/Info.plist";
                                INSTALL_PATH = /AppleInternal/XCTests/com.apple.mDNSResponder/;
                                LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
                                MTL_FAST_MATH = YES;
index 91323065d3981985b1a1c770ee76b824e2082e47..f208d28781c4a18e425eadce7936947a67b4cbd3 100644 (file)
@@ -66,7 +66,7 @@
  */
 
 #ifndef _DNS_SD_H
-#define _DNS_SD_H 10960002
+#define _DNS_SD_H 10964007
 
 #ifdef  __cplusplus
 extern "C" {
index e6224a275ba04b1fae3f18436323324e270277c4..1982982ec1cba1f997fa48fd68f194fc3bd1c4eb 100644 (file)
@@ -541,10 +541,6 @@ mDNSlocal AuthRecord *read_rr_from_ipc_msg(request_state *request, int GetTTL, i
         LogMsg("ERROR: Bad resource record flags (must be one of either kDNSServiceFlagsShared, kDNSServiceFlagsUnique or kDNSServiceFlagsKnownUnique)");
         return NULL;
     }
-
-    rr = (AuthRecord *) callocL("AuthRecord/read_rr_from_ipc_msg", sizeof(AuthRecord) - sizeof(RDataBody) + storage_size);
-    if (!rr) FatalError("ERROR: calloc");
-
     InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
 
     // The registration is scoped to a specific interface index, but the interface is not currently on our list.
@@ -561,6 +557,9 @@ mDNSlocal AuthRecord *read_rr_from_ipc_msg(request_state *request, int GetTTL, i
         return NULL;
 #endif
     }
+    rr = (AuthRecord *) callocL("AuthRecord/read_rr_from_ipc_msg", sizeof(AuthRecord) - sizeof(RDataBody) + storage_size);
+    if (!rr) FatalError("ERROR: calloc");
+
     if (InterfaceID == mDNSInterface_LocalOnly)
         artype = AuthRecordLocalOnly;
     else if (InterfaceID == mDNSInterface_P2P || InterfaceID == mDNSInterface_BLE)
index 774b8e9ba1afe65d4265fb8ec2858a869f8d51ad..66216c4f0a8705fb7a6085d3976f3b4e64d1c910 100644 (file)
@@ -117,6 +117,48 @@ mDNSexport void receive_response(const request_state* req, DNSMessage *msg, size
        mDNSCoreReceive(m, msg, end, &srcaddr, srcport, &primary_v4, dstport, primary_interfaceID);
 }
 
+mDNSexport void receive_suspicious_response_ut(const request_state* req, DNSMessage *msg, size_t msgSize, mDNSOpaque16 suspiciousqid, mDNSBool goodLastQID)
+{
+    mDNS *m = &mDNSStorage;
+    mDNSAddr srcaddr;
+    mDNSIPPort srcport, dstport;
+    const mDNSu8 * end;
+    DNSQuestion *q = (DNSQuestion *)&req->u.queryrecord.op.q;
+    UInt8* data = (UInt8*)msg;
+
+    // Used same values for DNS server as specified during init of unit test
+    srcaddr.type                = mDNSAddrType_IPv4;
+    srcaddr.ip.v4.NotAnInteger    = dns_server_ipv4.NotAnInteger;
+    srcport.NotAnInteger        = client_resp_src_port;
+
+    // Used random value for dstport
+    dstport.NotAnInteger = swap16((mDNSu16)client_resp_dst_port);
+
+    // Set DNS message (that was copied from a WireShark packet)
+    end = (const mDNSu8 *)msg + msgSize;
+
+    // Set socket info that mDNSCoreReceive uses to verify socket context
+    q->LocalSocket->ss.port.NotAnInteger = swap16((mDNSu16)client_resp_dst_port);
+    if (suspiciousqid.NotAnInteger)
+    {
+        q->TargetQID.NotAnInteger = swap16(suspiciousqid.NotAnInteger);
+        if (goodLastQID)
+        {
+            q->LastTargetQID.b[0] = data[0];
+            q->LastTargetQID.b[1] = data[1];
+        }
+        else q->LastTargetQID.NotAnInteger = 0;
+    }
+    else
+    {
+        q->TargetQID.b[0] = data[0];
+        q->TargetQID.b[1] = data[1];
+    }
+
+    // Execute mDNSCoreReceive which copies two DNS records into the cache
+    mDNSCoreReceive(m, msg, end, &srcaddr, srcport, &primary_v4, dstport, primary_interfaceID);
+}
+
 mDNSexport  size_t get_reply_len(char* name, uint16_t rdlen)
 {
        size_t len = sizeof(DNSServiceFlags);
@@ -156,7 +198,7 @@ mDNSexport void get_ip(const char *const name, struct sockaddr_storage *result)
 }
 
 // The AddDNSServer_ut function adds a dns server to mDNSResponder's list.
-mDNSexport mStatus AddDNSServer_ut(void)
+mDNSexport mStatus AddDNSServerScoped_ut(mDNSInterfaceID interfaceID, ScopeType scoped)
 {
     mDNS *m = &mDNSStorage;
     m->timenow = 0;
@@ -165,7 +207,6 @@ mDNSexport mStatus AddDNSServer_ut(void)
     mDNSAddr    addr;
     mDNSIPPort  port;
     mDNSs32     serviceID      = 0;
-    mDNSu32     scoped         = 0;
     mDNSu32     timeout        = dns_server_timeout;
     mDNSBool    cellIntf       = 0;
     mDNSBool    isExpensive    = 0;
@@ -179,9 +220,48 @@ mDNSexport mStatus AddDNSServer_ut(void)
     addr.type                  = mDNSAddrType_IPv4;
     addr.ip.v4.NotAnInteger    = dns_server_ipv4.NotAnInteger;
     port.NotAnInteger          = client_resp_src_port;
-    mDNS_AddDNSServer(m, &d, primary_interfaceID, serviceID, &addr, port, scoped, timeout,
+    mDNS_AddDNSServer(m, &d, interfaceID, serviceID, &addr, port, scoped, timeout,
                       cellIntf, isExpensive, isConstrained, isCLAT46, resGroupID,
                       reqA, reqAAAA, reqDO);
     mDNS_Unlock(m);
     return mStatus_NoError;
 }
+
+mDNSexport mStatus AddDNSServer_ut(void)
+{
+    return AddDNSServerScoped_ut(primary_interfaceID, kScopeNone);
+}
+
+mDNSexport mStatus  force_uDNS_SetupDNSConfig_ut(mDNS *const m)
+{
+    m->p->LastConfigGeneration = 0;
+    return uDNS_SetupDNSConfig(m);
+}
+
+mDNSexport mStatus verify_cache_addr_order_for_domain_ut(mDNS *const m, mDNSu8* octet, mDNSu32 count, const domainname *const name)
+{
+    mStatus result = mStatus_NoError;
+    const CacheGroup *cg = CacheGroupForName(m, DomainNameHashValue(name), name);
+    if (cg)
+    {
+        mDNSu32 i;
+        CacheRecord **rp = (CacheRecord **)&cg->members;
+        for (i = 0 ; *rp && i < count ; i++ )
+        {
+            if ((*rp)->resrec.rdata->u.ipv4.b[3] != octet[i])
+            {
+                LogInfo ("Octet %d compare failed %d != %d", i, (*rp)->resrec.rdata->u.ipv4.b[3], octet[i]);
+                break;
+            }
+            rp = &(*rp)->next;
+        }
+        if (i != count) result = mStatus_Invalid;
+    }
+    else
+    {
+        LogInfo ("Cache group not found");
+        result = mStatus_Invalid;
+    }
+
+    return result;
+}
index b22c10441d4fcf9d1a66483f83dc3a810c427510..3ab4b4f3b1d4a4f2f82460bd40d4c364c7dc969a 100644 (file)
@@ -37,6 +37,7 @@ extern mStatus  init_mdns_storage(void);
 extern size_t   get_reply_len(char* name, uint16_t rdlen);
 extern mStatus  start_client_request(request_state* req, char *msgbuf, size_t msgsz, uint32_t op, UDPSocket* socket);
 extern void     receive_response(const request_state* req, DNSMessage *msg, size_t msgSize);
+extern void     receive_suspicious_response_ut(const request_state* req, DNSMessage *msg, size_t msgSize, mDNSOpaque16 suspiciousqid, mDNSBool goodLastQID);
 extern void     get_ip(const char *const name, struct sockaddr_storage *result);
 extern void     free_req(request_state* req);
 
@@ -53,6 +54,9 @@ extern mDNSBool mDNSMacOSXCreateEtcHostsEntry_ut(const domainname *domain, const
                                                  const domainname *cname, char *ifname, AuthHash *auth);
 extern void     UpdateEtcHosts_ut(void *context);
 extern mStatus  AddDNSServer_ut(void);
+extern mStatus  AddDNSServerScoped_ut(mDNSInterfaceID interfaceID, ScopeType scoped);
+extern mStatus  force_uDNS_SetupDNSConfig_ut(mDNS *const m);
+extern mStatus  verify_cache_addr_order_for_domain_ut(mDNS *const m, mDNSu8* octet, mDNSu32 count, const domainname *const name);
 
 // HelperFunctionTest
 extern void mDNSDomainLabelFromCFString_ut(CFStringRef cfs, domainlabel *const namelabel);