};
#endif // TARGET_OS_DARWIN
-#if ( MDNSRESPONDER_PROJECT )
+#if( MDNSRESPONDER_PROJECT )
//===========================================================================================================================
// InterfaceMonitor
//===========================================================================================================================
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
//===========================================================================================================================
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
//===========================================================================================================================
// ExpensiveConstrainedTestCmd
//===========================================================================================================================
+
static void ExpensiveConstrainedTestCmd( void )
{
OSStatus err;
}
//===========================================================================================================================
-// RegistrationTestCmd
+// RegistrationTestCmd
//===========================================================================================================================
typedef struct RegistrationSubtest RegistrationSubtest;
include $(MAKEFILEPATH)/pb_makefiles/platform.make
-MVERS = "mDNSResponder-1096.0.2"
+MVERS = "mDNSResponder-1096.40.7"
VER =
ifneq ($(strip $(GCC_VERSION)),)
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
{
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);
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);
}
}
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;
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
}
}
}
- break;
+ break; // Check usage of RefreshCacheRecordCacheGroupOrder before removing (See note above)
}
else
{
#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
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.)
mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set)
#endif
{
- NetworkInterfaceInfo *primary;
const domainname *hostname;
mDNSRecordCallback *hostnameCallback;
AuthRecord *addrAR;
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)
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.
#endif
NetworkInterfaceInfo **p = &m->HostInterfaces;
mDNSBool revalidate = mDNSfalse;
- NetworkInterfaceInfo *primary;
NetworkInterfaceInfo *intf;
- AuthRecord *A;
mDNS_Lock(m);
// 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);
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
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);
}
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
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;
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
#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.
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."
+++ /dev/null
-/*
- * 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, ¬Used);
- 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, ¬Used);
- 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
+++ /dev/null
-/*
- * 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
+++ /dev/null
-//
-// 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
+++ /dev/null
-<?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>
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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
--- /dev/null
+/*
+ * 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, ¬Used);
+ 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, ¬Used);
+ 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
--- /dev/null
+/*
+ * 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, ¬Used);
+ 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, ¬Used);
+ 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+//
+// 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
--- /dev/null
+<?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>
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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, ¬Used);
+ 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, ¬Used);
+ 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
--- /dev/null
+/*
+ * 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
+++ /dev/null
-/*
- * 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
</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>
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)
{
*val = 0;
CFRelease(dict);
}
-
}
+#endif
#if APPLE_OSX_mDNSResponder
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");
LogSPS("SystemWakeForNetworkAccess: Wake On LAN: %d", ret);
return ret;
+#endif
}
mDNSlocal mDNSBool SystemSleepOnlyIfWakeOnLAN(void)
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;
*/
#ifndef _DNS_SD_H
-#define _DNS_SD_H 10960002
+#define _DNS_SD_H 10964007
#ifdef __cplusplus
extern "C" {
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.
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)
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);
}
// 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;
mDNSAddr addr;
mDNSIPPort port;
mDNSs32 serviceID = 0;
- mDNSu32 scoped = 0;
mDNSu32 timeout = dns_server_timeout;
mDNSBool cellIntf = 0;
mDNSBool isExpensive = 0;
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;
+}
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);
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);