summaryrefslogtreecommitdiffstats
path: root/mDNSResponder/unittests
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2018-09-19 08:56:09 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2018-09-20 11:23:33 +0200
commit4c086a244624bf36865edcfa4309c333d7d7200d (patch)
treee566ffc50a6b6fdb46702ac57c8e7c4088b60b42 /mDNSResponder/unittests
parentmDNSResponder: Update to v765.50.9 (diff)
downloadrtems-libbsd-4c086a244624bf36865edcfa4309c333d7d7200d.tar.bz2
mDNSResponder: Update to v878.1.1
The sources can be obtained via: https://opensource.apple.com/tarballs/mDNSResponder/mDNSResponder-878.1.1.tar.gz Update #3522.
Diffstat (limited to 'mDNSResponder/unittests')
-rw-r--r--mDNSResponder/unittests/CNameRecordTests.c400
-rw-r--r--mDNSResponder/unittests/CNameRecordTests.h9
-rw-r--r--mDNSResponder/unittests/LocalOnlyTimeoutTests.c378
-rw-r--r--mDNSResponder/unittests/LocalOnlyTimeoutTests.h9
-rw-r--r--mDNSResponder/unittests/ResourceRecordTest.c5
-rw-r--r--mDNSResponder/unittests/daemon_ut.c35
-rw-r--r--mDNSResponder/unittests/mDNSCoreReceiveTest.c151
-rw-r--r--mDNSResponder/unittests/mDNSCoreReceiveTest.h9
-rw-r--r--mDNSResponder/unittests/main.c13
-rw-r--r--mDNSResponder/unittests/mdns_macosx_ut.c66
-rw-r--r--mDNSResponder/unittests/mdns_ut.c17
-rw-r--r--mDNSResponder/unittests/uds_daemon_ut.c43
-rw-r--r--mDNSResponder/unittests/unittest.h44
-rw-r--r--mDNSResponder/unittests/unittest_common.c157
-rw-r--r--mDNSResponder/unittests/unittest_common.h57
15 files changed, 1387 insertions, 6 deletions
diff --git a/mDNSResponder/unittests/CNameRecordTests.c b/mDNSResponder/unittests/CNameRecordTests.c
new file mode 100644
index 00000000..9b3fb016
--- /dev/null
+++ b/mDNSResponder/unittests/CNameRecordTests.c
@@ -0,0 +1,400 @@
+#include "CNameRecordTests.h"
+#include "unittest_common.h"
+
+mDNSlocal int InitThisUnitTest(void);
+mDNSlocal int StartClientQueryRequest(void);
+mDNSlocal int PopulateCacheWithClientResponseRecords(void);
+mDNSlocal int SimulateNetworkChangeAndVerifyTest(void);
+mDNSlocal int FinalizeUnitTest(void);
+mDNSlocal mStatus AddDNSServer(void);
+
+// This unit test's variables
+static UDPSocket* local_socket;
+static request_state* client_request_message;
+
+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 }};
+
+UNITTEST_HEADER(CNameRecordTests)
+ UNITTEST_TEST(InitThisUnitTest)
+ UNITTEST_TEST(StartClientQueryRequest)
+ UNITTEST_TEST(PopulateCacheWithClientResponseRecords)
+ UNITTEST_TEST(SimulateNetworkChangeAndVerifyTest)
+ UNITTEST_TEST(FinalizeUnitTest)
+UNITTEST_FOOTER
+
+// 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.
+UNITTEST_HEADER(InitThisUnitTest)
+ // Init unit test environment and verify no error occurred.
+ mStatus result = init_mdns_environment(mDNStrue);
+ UNITTEST_ASSERT(result == mStatus_NoError);
+
+ // Add one DNS server and verify it was added.
+ AddDNSServer();
+ UNITTEST_ASSERT(NumUnicastDNSServers == 1);
+
+ // Create memory for a socket that is never used or opened.
+ local_socket = mDNSPlatformMemAllocate(sizeof(UDPSocket));
+ mDNSPlatformMemZero(local_socket, sizeof(UDPSocket));
+
+ // Create memory for a request that is used to make this unit test's client request.
+ client_request_message = calloc(1, sizeof(request_state));
+UNITTEST_FOOTER
+
+// 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.
+UNITTEST_HEADER(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);
+ UNITTEST_ASSERT(err == mStatus_NoError);
+
+ // Verify the request fields were set as expected
+ UNITTEST_ASSERT(req->next == mDNSNULL);
+ UNITTEST_ASSERT(req->primary == mDNSNULL);
+ UNITTEST_ASSERT(req->sd == client_req_sd);
+ UNITTEST_ASSERT(req->process_id == client_req_process_id);
+ UNITTEST_ASSERT(!strcmp(req->pid_name, client_req_pid_name));
+ UNITTEST_ASSERT(req->validUUID == mDNSfalse);
+ UNITTEST_ASSERT(req->errsd == 0);
+ UNITTEST_ASSERT(req->uid == client_req_uid);
+ UNITTEST_ASSERT(req->ts == t_complete);
+ UNITTEST_ASSERT((mDNSs32)req->data_bytes > min_size);
+ UNITTEST_ASSERT(req->msgend == msgptr+msgsz);
+ UNITTEST_ASSERT(req->msgbuf == mDNSNULL);
+ UNITTEST_ASSERT(req->hdr.version == VERSION);
+ UNITTEST_ASSERT(req->replies == mDNSNULL);
+ UNITTEST_ASSERT(req->terminate != mDNSNULL);
+ UNITTEST_ASSERT(req->flags == kDNSServiceFlagsReturnIntermediates);
+ UNITTEST_ASSERT(req->interfaceIndex == kDNSServiceInterfaceIndexAny);
+
+ // Verify the query fields were set as expected
+ q = &req->u.queryrecord.q;
+ UNITTEST_ASSERT(q != mDNSNULL);
+ UNITTEST_ASSERT(q == m->Questions);
+ UNITTEST_ASSERT(q == m->NewQuestions);
+ UNITTEST_ASSERT(q->SuppressUnusable == mDNSfalse);
+ UNITTEST_ASSERT(q->ReturnIntermed == mDNStrue);
+ UNITTEST_ASSERT(q->SuppressQuery == mDNSfalse);
+
+ UNITTEST_ASSERT(q->qnameOrig == mDNSNULL);
+ ConvertDomainNameToCString(&q->qname, qname_cstr);
+ UNITTEST_ASSERT(!strcmp(qname_cstr, udns_original_domainname_cstr));
+ UNITTEST_ASSERT(q->qnamehash == DomainNameHashValue(&q->qname));
+
+ UNITTEST_ASSERT(q->InterfaceID == mDNSInterface_Any);
+ UNITTEST_ASSERT(q->flags == req->flags);
+ UNITTEST_ASSERT(q->qtype == 1);
+ UNITTEST_ASSERT(q->qclass == 1);
+ UNITTEST_ASSERT(q->LongLived == 0);
+ UNITTEST_ASSERT(q->ExpectUnique == mDNSfalse);
+ UNITTEST_ASSERT(q->ForceMCast == 0);
+ UNITTEST_ASSERT(q->TimeoutQuestion == 0);
+ UNITTEST_ASSERT(q->WakeOnResolve == 0);
+ UNITTEST_ASSERT(q->UseBackgroundTrafficClass == 0);
+ UNITTEST_ASSERT(q->ValidationRequired == 0);
+ UNITTEST_ASSERT(q->ValidatingResponse == 0);
+ UNITTEST_ASSERT(q->ProxyQuestion == 0);
+ UNITTEST_ASSERT(q->AnonInfo == mDNSNULL);
+ UNITTEST_ASSERT(q->QuestionCallback != mDNSNULL);
+ UNITTEST_ASSERT(q->QuestionContext == req);
+ UNITTEST_ASSERT(q->SearchListIndex == 0);
+ UNITTEST_ASSERT(q->DNSSECAuthInfo == mDNSNULL);
+ UNITTEST_ASSERT(q->DAIFreeCallback == mDNSNULL);
+ UNITTEST_ASSERT(q->RetryWithSearchDomains == 0);
+ UNITTEST_ASSERT(q->AppendSearchDomains == 0);
+ UNITTEST_ASSERT(q->AppendLocalSearchDomains == 0);
+ UNITTEST_ASSERT(q->DuplicateOf == mDNSNULL);
+
+ // 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.
+ UNITTEST_ASSERT(m->NewQuestions == mDNSNULL);
+
+ // Verify the cache is empty and the request got no reply.
+ UNITTEST_ASSERT(m->rrcache_totalused == 0);
+ UNITTEST_ASSERT(req->replies == mDNSNULL);
+
+UNITTEST_FOOTER
+
+// 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.
+UNITTEST_HEADER(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.q;
+ const char *data;
+ const char *end;
+ char name[kDNSServiceMaxDomainName];
+ uint16_t rrtype, rrclass, rdlen;
+ const char *rdata;
+ size_t len;
+ char domainname_cstr[MAX_ESCAPED_DOMAIN_NAME];
+
+ // Receive and populate the cache with canned response
+ receive_response(req, msgptr, msgsz);
+
+ // Verify 2 cache entries for CName and A record are present
+ mDNSu32 CacheUsed =0, notUsed =0;
+ LogCacheRecords_ut(mDNS_TimeNow(m), &CacheUsed, &notUsed);
+ UNITTEST_ASSERT(CacheUsed == m->rrcache_totalused);
+ UNITTEST_ASSERT(CacheUsed == 4); // 2 for the CacheGroup object plus 2 for the A and CNAME records
+ UNITTEST_ASSERT(m->PktNum == 1); // one packet was received
+
+ // Verify question's qname is now set with the A record's domainname
+ UNITTEST_ASSERT(q->qnameOrig == mDNSNULL);
+ ConvertDomainNameToCString(&q->qname, domainname_cstr);
+ UNITTEST_ASSERT(q->qnamehash == DomainNameHashValue(&q->qname));
+ UNITTEST_ASSERT(!strcmp(domainname_cstr, udns_cname_domainname_cstr));
+
+ // Verify client's add event for CNAME is properly formed
+ reply = req->replies;
+ UNITTEST_ASSERT(reply != mDNSNULL);
+ UNITTEST_ASSERT(reply->next == mDNSNULL);
+
+ 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);
+
+ UNITTEST_ASSERT(reply->totallen == len + sizeof(ipc_msg_hdr));
+ UNITTEST_ASSERT(reply->mhdr->version == VERSION);
+ UNITTEST_ASSERT(reply->mhdr->datalen == len);
+ UNITTEST_ASSERT(reply->mhdr->ipc_flags == 0);
+ UNITTEST_ASSERT(reply->mhdr->op == query_reply_op);
+
+ UNITTEST_ASSERT(reply->rhdr->flags == htonl(kDNSServiceFlagsAdd));
+ UNITTEST_ASSERT(reply->rhdr->ifi == kDNSServiceInterfaceIndexAny);
+ UNITTEST_ASSERT(reply->rhdr->error == kDNSServiceErr_NoError);
+
+ UNITTEST_ASSERT(rrtype == kDNSType_CNAME);
+ UNITTEST_ASSERT(rrclass == kDNSClass_IN);
+ ConvertDomainNameToCString((const domainname *const)rdata, domainname_cstr);
+ UNITTEST_ASSERT(!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;
+ UNITTEST_ASSERT(reply != mDNSNULL);
+ UNITTEST_ASSERT(reply->next != 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);
+
+ UNITTEST_ASSERT(reply->totallen == len + sizeof(ipc_msg_hdr));
+ UNITTEST_ASSERT(reply->mhdr->version == VERSION);
+ UNITTEST_ASSERT(reply->mhdr->datalen == len);
+
+ UNITTEST_ASSERT(reply->mhdr->ipc_flags == 0);
+ UNITTEST_ASSERT(reply->mhdr->op == query_reply_op);
+
+ UNITTEST_ASSERT(reply->rhdr->flags == htonl(kDNSServiceFlagsAdd));
+ UNITTEST_ASSERT(reply->rhdr->ifi == kDNSServiceInterfaceIndexAny);
+ UNITTEST_ASSERT(reply->rhdr->error == kDNSServiceErr_NoError);
+
+ UNITTEST_ASSERT(rrtype == kDNSType_A);
+ UNITTEST_ASSERT(rrclass == kDNSClass_IN);
+ UNITTEST_ASSERT(rdata[0] == dns_response_ipv4.b[0]);
+ UNITTEST_ASSERT(rdata[1] == dns_response_ipv4.b[1]);
+ UNITTEST_ASSERT(rdata[2] == dns_response_ipv4.b[2]);
+ UNITTEST_ASSERT(rdata[3] == dns_response_ipv4.b[3]);
+
+UNITTEST_FOOTER
+
+// 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.
+UNITTEST_HEADER(SimulateNetworkChangeAndVerifyTest)
+ 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.
+ UNITTEST_ASSERT(q->ThisQInterval == InitialQuestionInterval);
+ UNITTEST_ASSERT(q->TargetQID.NotAnInteger != uDNS_TargetQID);
+
+ // Then mDNS_Execute removes both records from the cache and calls the client back with a remove event for A record.
+ m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
+ mDNS_Execute(m);
+
+ // Verify the cache entries are removed
+ LogCacheRecords_ut(mDNS_TimeNow(m), &CacheUsed, &notUsed);
+ UNITTEST_ASSERT(CacheUsed == m->rrcache_totalused);
+ UNITTEST_ASSERT(CacheUsed == 0);
+
+ // Verify the A record's remove event is setup as expected in the reply data
+ struct reply_state *reply;
+ reply = req->replies;
+ UNITTEST_ASSERT(reply != mDNSNULL);
+
+ UNITTEST_ASSERT(reply != mDNSNULL);
+ UNITTEST_ASSERT(reply->next != mDNSNULL);
+ UNITTEST_ASSERT(reply->next->next != 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);
+
+ UNITTEST_ASSERT(reply->totallen == reply->mhdr->datalen + sizeof(ipc_msg_hdr));
+ UNITTEST_ASSERT(reply->mhdr->version == VERSION);
+ UNITTEST_ASSERT(reply->mhdr->datalen == len);
+ UNITTEST_ASSERT(reply->mhdr->ipc_flags == 0);
+ UNITTEST_ASSERT(reply->mhdr->op == query_reply_op);
+
+ UNITTEST_ASSERT(reply->rhdr->flags != htonl(kDNSServiceFlagsAdd));
+ UNITTEST_ASSERT(reply->rhdr->ifi == kDNSServiceInterfaceIndexAny);
+ UNITTEST_ASSERT(reply->rhdr->error == kDNSServiceErr_NoError);
+
+ UNITTEST_ASSERT(rrtype == kDNSType_A);
+ UNITTEST_ASSERT(rrclass == kDNSClass_IN);
+ UNITTEST_ASSERT(rdata[0] == dns_response_ipv4.b[0]);
+ UNITTEST_ASSERT(rdata[1] == dns_response_ipv4.b[1]);
+ UNITTEST_ASSERT(rdata[2] == dns_response_ipv4.b[2]);
+ UNITTEST_ASSERT(rdata[3] == dns_response_ipv4.b[3]);
+
+UNITTEST_FOOTER
+
+// This function does memory cleanup and no verification.
+UNITTEST_HEADER(FinalizeUnitTest)
+ 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);
+ }
+UNITTEST_FOOTER
+
+// The mDNS_AddDNSServer function adds a dns server to mDNSResponder's list.
+mDNSlocal mStatus AddDNSServer(void)
+{
+ mDNS *m = &mDNSStorage;
+ m->timenow = 0;
+ mDNS_Lock(m);
+ domainname d;
+ mDNSAddr addr;
+ mDNSIPPort port;
+ mDNSs32 serviceID = 0;
+ mDNSu32 scoped = 0;
+ mDNSu32 timeout = dns_server_timeout;
+ mDNSBool cellIntf = 0;
+ mDNSBool isExpensive = 0;
+ mDNSu16 resGroupID = dns_server_resGroupID;
+ mDNSBool reqA = mDNStrue;
+ mDNSBool reqAAAA = mDNStrue;
+ mDNSBool reqDO = mDNSfalse;
+ d.c[0] = 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,
+ cellIntf, isExpensive, resGroupID,
+ reqA, reqAAAA, reqDO);
+ mDNS_Unlock(m);
+ return mStatus_NoError;
+}
+
+
diff --git a/mDNSResponder/unittests/CNameRecordTests.h b/mDNSResponder/unittests/CNameRecordTests.h
new file mode 100644
index 00000000..2c2a3a1c
--- /dev/null
+++ b/mDNSResponder/unittests/CNameRecordTests.h
@@ -0,0 +1,9 @@
+
+#ifndef ReconfirmRecordTests_h
+#define ReconfirmRecordTests_h
+
+#include "unittest.h"
+
+int CNameRecordTests(void);
+
+#endif /* ReconfirmRecordTests_h */
diff --git a/mDNSResponder/unittests/LocalOnlyTimeoutTests.c b/mDNSResponder/unittests/LocalOnlyTimeoutTests.c
new file mode 100644
index 00000000..e2959480
--- /dev/null
+++ b/mDNSResponder/unittests/LocalOnlyTimeoutTests.c
@@ -0,0 +1,378 @@
+#include "LocalOnlyTimeoutTests.h"
+#include "unittest_common.h"
+
+mDNSlocal int InitUnitTest(void);
+mDNSlocal int StartLocalOnlyClientQueryRequest(void);
+mDNSlocal int PopulateCacheWithClientLOResponseRecords(void);
+mDNSlocal int RestartLocalOnlyClientQueryRequest(void);
+mDNSlocal int FinalizeUnitTest(void);
+mDNSlocal mStatus InitEtcHostsRecords();
+
+// This unit test's variables
+static request_state* client_request_message;
+static UDPSocket* local_socket;
+static char domainname_cstr[MAX_ESCAPED_DOMAIN_NAME];
+
+// 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
+};
+
+UNITTEST_HEADER(LocalOnlyTimeoutTests)
+ UNITTEST_TEST(InitUnitTest)
+ UNITTEST_TEST(StartLocalOnlyClientQueryRequest)
+ UNITTEST_TEST(PopulateCacheWithClientLOResponseRecords)
+ UNITTEST_TEST(RestartLocalOnlyClientQueryRequest)
+ UNITTEST_TEST(FinalizeUnitTest)
+UNITTEST_FOOTER
+
+// 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.
+UNITTEST_HEADER(InitUnitTest)
+
+ // Init mDNSStorage
+ mStatus result = init_mdns_storage();
+ if (result != mStatus_NoError)
+ return result;
+
+ // Allocate a client request
+ local_socket = calloc(1, sizeof(request_state));
+
+ // 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));
+
+UNITTEST_FOOTER
+
+// 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.
+UNITTEST_HEADER(StartLocalOnlyClientQueryRequest)
+
+ 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);
+ UNITTEST_ASSERT(err == mStatus_NoError);
+
+ // Verify the query initialized and request fields were set as expected
+ UNITTEST_ASSERT(err == mStatus_NoError);
+ UNITTEST_ASSERT(req->hdr.version == VERSION);
+ UNITTEST_ASSERT((mDNSs32)req->data_bytes > min_size);
+ UNITTEST_ASSERT(req->flags == (kDNSServiceFlagsSuppressUnusable | kDNSServiceFlagsReturnIntermediates | kDNSServiceFlagsTimeout));
+ UNITTEST_ASSERT(req->interfaceIndex == kDNSServiceInterfaceIndexLocalOnly);
+ UNITTEST_ASSERT(req->terminate != mDNSNULL);
+
+ q = &req->u.queryrecord.q;
+ UNITTEST_ASSERT(q == m->NewLocalOnlyQuestions);
+ UNITTEST_ASSERT(m->Questions == NULL);
+ UNITTEST_ASSERT(m->NewQuestions == NULL);
+ UNITTEST_ASSERT(q->SuppressUnusable == 1);
+ UNITTEST_ASSERT(q->ReturnIntermed == 1);
+ UNITTEST_ASSERT(q->SuppressQuery == 0); // Regress <rdar://problem/27571734>
+
+ UNITTEST_ASSERT(q->qnameOrig == mDNSNULL);
+ ConvertDomainNameToCString(&q->qname, qname_cstr);
+ UNITTEST_ASSERT(!strcmp(qname_cstr, domainname_cstr));
+ UNITTEST_ASSERT(q->qnamehash == DomainNameHashValue(&q->qname));
+
+ UNITTEST_ASSERT(q->InterfaceID == mDNSInterface_LocalOnly);
+ UNITTEST_ASSERT(q->flags == req->flags);
+ UNITTEST_ASSERT(q->qtype == 1);
+ UNITTEST_ASSERT(q->qclass == 1);
+ UNITTEST_ASSERT(q->LongLived == 0);
+ UNITTEST_ASSERT(q->ExpectUnique == mDNSfalse);
+ UNITTEST_ASSERT(q->ForceMCast == 0);
+ UNITTEST_ASSERT(q->TimeoutQuestion == 1);
+ UNITTEST_ASSERT(q->WakeOnResolve == 0);
+ UNITTEST_ASSERT(q->UseBackgroundTrafficClass == 0);
+ UNITTEST_ASSERT(q->ValidationRequired == 0);
+ UNITTEST_ASSERT(q->ValidatingResponse == 0);
+ UNITTEST_ASSERT(q->ProxyQuestion == 0);
+ UNITTEST_ASSERT(q->AnonInfo == mDNSNULL);
+ UNITTEST_ASSERT(q->QuestionCallback != mDNSNULL);
+ UNITTEST_ASSERT(q->QuestionContext == req);
+ UNITTEST_ASSERT(q->SearchListIndex == 0);
+ UNITTEST_ASSERT(q->DNSSECAuthInfo == mDNSNULL);
+ UNITTEST_ASSERT(q->DAIFreeCallback == mDNSNULL);
+ UNITTEST_ASSERT(q->RetryWithSearchDomains == 0);
+ UNITTEST_ASSERT(q->StopTime != 0);
+ UNITTEST_ASSERT(q->AppendSearchDomains == 0);
+ UNITTEST_ASSERT(q->AppendLocalSearchDomains == 0);
+ UNITTEST_ASSERT(q->DuplicateOf == mDNSNULL);
+
+ // 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;
+ UNITTEST_ASSERT(reply != mDNSNULL);
+
+ UNITTEST_ASSERT(m->NewLocalOnlyQuestions == NULL);
+ UNITTEST_ASSERT(q->LOAddressAnswers == 0);
+
+ len = get_reply_len(qname_cstr, 0);
+
+ UNITTEST_ASSERT(reply->next == mDNSNULL);
+ UNITTEST_ASSERT(reply->totallen == reply->mhdr->datalen + sizeof(ipc_msg_hdr));
+ UNITTEST_ASSERT(reply->mhdr->version == VERSION);
+ UNITTEST_ASSERT(reply->mhdr->datalen == len);
+ UNITTEST_ASSERT(reply->mhdr->ipc_flags == 0);
+ UNITTEST_ASSERT(reply->mhdr->op == query_reply_op);
+
+ UNITTEST_ASSERT(reply->rhdr->flags == htonl(kDNSServiceFlagsAdd));
+ UNITTEST_ASSERT(reply->rhdr->ifi == kDNSServiceInterfaceIndexLocalOnly); // Regress <rdar://problem/27340874>
+ UNITTEST_ASSERT(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;
+ UNITTEST_ASSERT(reply != NULL);
+ UNITTEST_ASSERT(m->NewLocalOnlyQuestions == NULL);
+ UNITTEST_ASSERT(q->LOAddressAnswers == 0);
+
+ len = get_reply_len(qname_cstr, 0);
+
+ UNITTEST_ASSERT(reply->next == mDNSNULL);
+ UNITTEST_ASSERT(reply->totallen == len + sizeof(ipc_msg_hdr));
+ UNITTEST_ASSERT(reply->mhdr->version == VERSION);
+ UNITTEST_ASSERT(reply->mhdr->datalen == len);
+ UNITTEST_ASSERT(reply->mhdr->ipc_flags == 0);
+ UNITTEST_ASSERT(reply->mhdr->op == query_reply_op);
+ UNITTEST_ASSERT(reply->rhdr->flags == htonl(kDNSServiceFlagsAdd));
+ UNITTEST_ASSERT(reply->rhdr->ifi == kDNSServiceInterfaceIndexLocalOnly); // Regress <rdar://problem/27340874>
+ UNITTEST_ASSERT(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));
+
+UNITTEST_FOOTER
+
+// This unit test populates the cache with four /etc/hosts records and then
+// verifies there are four entries in the cache.
+UNITTEST_HEADER(PopulateCacheWithClientLOResponseRecords)
+
+ mDNS *const m = &mDNSStorage;
+
+ // Verify cache is empty
+ int count = LogEtcHosts_ut(m);
+ UNITTEST_ASSERT(count == 0);
+
+ // Populate /etc/hosts
+ mStatus result = InitEtcHostsRecords();
+ UNITTEST_ASSERT(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);
+ UNITTEST_ASSERT(count == 4);
+
+UNITTEST_FOOTER
+
+// 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.
+UNITTEST_HEADER(RestartLocalOnlyClientQueryRequest)
+
+ 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);
+ UNITTEST_ASSERT(err == mStatus_NoError);
+
+ UNITTEST_ASSERT(err == mStatus_NoError);
+ UNITTEST_ASSERT(req->hdr.version == VERSION);
+ UNITTEST_ASSERT((mDNSs32)req->data_bytes > min_size);
+ UNITTEST_ASSERT(req->flags == (kDNSServiceFlagsSuppressUnusable | kDNSServiceFlagsReturnIntermediates | kDNSServiceFlagsTimeout));
+ UNITTEST_ASSERT(req->interfaceIndex == kDNSServiceInterfaceIndexLocalOnly);
+ UNITTEST_ASSERT(req->terminate != mDNSNULL);
+ UNITTEST_ASSERT(m->Questions == NULL);
+
+ q = &req->u.queryrecord.q;
+ UNITTEST_ASSERT(q == m->NewLocalOnlyQuestions);
+ UNITTEST_ASSERT(q->SuppressUnusable == 1);
+ UNITTEST_ASSERT(q->ReturnIntermed == 1);
+ UNITTEST_ASSERT(q->SuppressQuery == 0); // Regress <rdar://problem/27571734>
+ UNITTEST_ASSERT(q->qnamehash == DomainNameHashValue(&q->qname));
+ UNITTEST_ASSERT(q->InterfaceID == mDNSInterface_LocalOnly);
+ UNITTEST_ASSERT(q->flags == req->flags);
+ UNITTEST_ASSERT(q->qtype == 1);
+ UNITTEST_ASSERT(q->qclass == 1);
+ UNITTEST_ASSERT(q->LongLived == 0);
+ UNITTEST_ASSERT(q->ExpectUnique == mDNSfalse);
+ UNITTEST_ASSERT(q->ForceMCast == 0);
+ UNITTEST_ASSERT(q->TimeoutQuestion == 1);
+ UNITTEST_ASSERT(q->WakeOnResolve == 0);
+ UNITTEST_ASSERT(q->UseBackgroundTrafficClass == 0);
+ UNITTEST_ASSERT(q->ValidationRequired == 0);
+ UNITTEST_ASSERT(q->ValidatingResponse == 0);
+ UNITTEST_ASSERT(q->ProxyQuestion == 0);
+ UNITTEST_ASSERT(q->AnonInfo == mDNSNULL);
+ UNITTEST_ASSERT(q->QuestionCallback != mDNSNULL);
+ UNITTEST_ASSERT(q->QuestionContext == req);
+ UNITTEST_ASSERT(q->SearchListIndex == 0);
+ UNITTEST_ASSERT(q->DNSSECAuthInfo == mDNSNULL);
+ UNITTEST_ASSERT(q->DAIFreeCallback == mDNSNULL);
+ UNITTEST_ASSERT(q->RetryWithSearchDomains == 0);
+ UNITTEST_ASSERT(q->StopTime != 0);
+ UNITTEST_ASSERT(q->AppendSearchDomains == 0);
+ UNITTEST_ASSERT(q->AppendLocalSearchDomains == 0);
+ UNITTEST_ASSERT(q->DuplicateOf == mDNSNULL);
+ ConvertDomainNameToCString(&q->qname, qname_cstr);
+ UNITTEST_ASSERT(!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>
+ UNITTEST_ASSERT(m->NewLocalOnlyQuestions == NULL);
+ UNITTEST_ASSERT(req->u.queryrecord.ans == 1);
+ UNITTEST_ASSERT(q->LOAddressAnswers == 1);
+ UNITTEST_ASSERT(q == m->LocalOnlyQuestions);
+
+ reply = req->replies;
+ len = get_reply_len(qname_cstr, 4);
+
+ UNITTEST_ASSERT(reply->next == mDNSNULL);
+ UNITTEST_ASSERT(reply->totallen == len + sizeof(ipc_msg_hdr));
+ UNITTEST_ASSERT(reply->mhdr->version == VERSION);
+ UNITTEST_ASSERT(reply->mhdr->datalen == len);
+ UNITTEST_ASSERT(reply->mhdr->ipc_flags == 0);
+ UNITTEST_ASSERT(reply->mhdr->op == query_reply_op);
+ UNITTEST_ASSERT(reply->rhdr->flags == htonl(kDNSServiceFlagsAdd));
+ UNITTEST_ASSERT(reply->rhdr->ifi == kDNSServiceInterfaceIndexLocalOnly); // Regress <rdar://problem/27340874>
+ UNITTEST_ASSERT(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;
+ UNITTEST_ASSERT(reply != NULL);
+ UNITTEST_ASSERT(reply->next == NULL);
+ UNITTEST_ASSERT(m->NewLocalOnlyQuestions == NULL);
+ UNITTEST_ASSERT(q->LOAddressAnswers == 0);
+ len = get_reply_len(qname_cstr, 0);
+
+ UNITTEST_ASSERT(reply->next == mDNSNULL);
+ UNITTEST_ASSERT(reply->totallen == len + + sizeof(ipc_msg_hdr));
+ UNITTEST_ASSERT(reply->mhdr->version == VERSION);
+ UNITTEST_ASSERT(reply->mhdr->datalen == len);
+ UNITTEST_ASSERT(reply->mhdr->ipc_flags == 0);
+ UNITTEST_ASSERT(reply->mhdr->op == query_reply_op);
+ UNITTEST_ASSERT(reply->rhdr->flags == htonl(kDNSServiceFlagsAdd));
+ UNITTEST_ASSERT(reply->rhdr->ifi == kDNSServiceInterfaceIndexLocalOnly); // Regress <rdar://problem/27340874>
+ UNITTEST_ASSERT(reply->rhdr->error ==
+ (DNSServiceErrorType)htonl(kDNSServiceErr_Timeout)); // Regress <rdar://problem/27562965>
+
+ free_req(req);
+UNITTEST_FOOTER
+
+// This function does memory cleanup and no verification.
+UNITTEST_HEADER(FinalizeUnitTest)
+ mDNSPlatformMemFree(local_socket);
+UNITTEST_FOOTER
+
+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;
+}
diff --git a/mDNSResponder/unittests/LocalOnlyTimeoutTests.h b/mDNSResponder/unittests/LocalOnlyTimeoutTests.h
new file mode 100644
index 00000000..3ff56082
--- /dev/null
+++ b/mDNSResponder/unittests/LocalOnlyTimeoutTests.h
@@ -0,0 +1,9 @@
+
+#ifndef LocalOnlyTimeoutTests_h
+#define LocalOnlyTimeoutTests_h
+
+#include "unittest.h"
+
+int LocalOnlyTimeoutTests(void);
+
+#endif /* LocalOnlyTimeoutTests_h */
diff --git a/mDNSResponder/unittests/ResourceRecordTest.c b/mDNSResponder/unittests/ResourceRecordTest.c
index d0530086..3c976c3d 100644
--- a/mDNSResponder/unittests/ResourceRecordTest.c
+++ b/mDNSResponder/unittests/ResourceRecordTest.c
@@ -18,7 +18,7 @@ UNITTEST_HEADER(TXTSetupTest)
AuthRecord authRec;
mDNS_SetupResourceRecord(&authRec, mDNSNULL, mDNSInterface_Any, kDNSType_TXT, kStandardTTL, kDNSRecordTypeShared, AuthRecordAny,mDNSNULL, mDNSNULL);
- UNITTEST_ASSERT_RETURN(authRec.resrec.RecordType == kDNSType_TXT);
+ // This fails >> UNITTEST_ASSERT_RETURN(authRec.resrec.RecordType == kDNSType_TXT);
UNITTEST_ASSERT_RETURN(authRec.resrec.rdata->MaxRDLength == sizeof(RDataBody));
// Retest with a RDataStorage set to a a buffer
@@ -28,7 +28,8 @@ UNITTEST_FOOTER
UNITTEST_HEADER(ASetupTest)
AuthRecord authRec;
mDNS_SetupResourceRecord(&authRec, mDNSNULL, mDNSInterface_Any, kDNSType_A, kHostNameTTL, kDNSRecordTypeUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
- UNITTEST_ASSERT_RETURN(authRec.resrec.RecordType == kDNSType_A);
+
+ // This fails >> UNITTEST_ASSERT_RETURN(authRec.resrec.RecordType == kDNSType_A);
// Add more verifications
UNITTEST_FOOTER
diff --git a/mDNSResponder/unittests/daemon_ut.c b/mDNSResponder/unittests/daemon_ut.c
new file mode 100644
index 00000000..526b3513
--- /dev/null
+++ b/mDNSResponder/unittests/daemon_ut.c
@@ -0,0 +1,35 @@
+#include "DNSCommon.h"
+
+mDNSexport void init_logging_ut(void)
+{
+#if APPLE_OSX_mDNSResponder
+ init_logging();
+
+ /* When doing unit testing, it is likely that some local functions and
+ * variables will not be needed to do unit testing validation. So to get
+ * around compiler warnings about unused functions or variables, each
+ * warning work-around is handled explicitly below.
+ */
+
+ /* The next three LogOperation() are used to trick the compiler into
+ * suppressing unused function and variable warnings. This is done by
+ * outputting the function or variable pointer to a log message.
+ */
+ LogOperation("Quiet compiler warnings for KQueueLoop= %p, "
+ "KQWokenFlushBytes= %p, SignalCallback= %p, "
+ "mDNS_StatusCallback= %p, LaunchdCheckin= %p",
+ KQueueLoop, KQWokenFlushBytes,
+ SignalCallback, mDNS_StatusCallback,
+ LaunchdCheckin);
+ LogOperation("Quiet compiler warnings for SandboxProcess= %p, "
+ "mDNSDaemonInitialize= %p, HandleSIG= %p, "
+ "PreferencesGetValueInt= %p, PreferencesGetValueBool= %p",
+ SandboxProcess, mDNSDaemonInitialize,
+ HandleSIG, PreferencesGetValueInt,
+ PreferencesGetValueBool);
+ LogOperation("Quiet compiler warnings for rrcachestorage= %p, "
+ "NoMulticastAdvertisements= %p",
+ rrcachestorage, NoMulticastAdvertisements);
+
+#endif // APPLE_OSX_mDNSResponder
+}
diff --git a/mDNSResponder/unittests/mDNSCoreReceiveTest.c b/mDNSResponder/unittests/mDNSCoreReceiveTest.c
new file mode 100644
index 00000000..ece21407
--- /dev/null
+++ b/mDNSResponder/unittests/mDNSCoreReceiveTest.c
@@ -0,0 +1,151 @@
+#include "mDNSCoreReceiveTest.h"
+#include "unittest_common.h"
+
+int InitmDNSCoreReceiveTest(void);
+int ValidQueryReqTest(void);
+int NullDstQueryReqTest(void);
+int ReceiveArpLogMsgTest(void);
+void InitmDNSStorage(mDNS *const m);
+
+// 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
+};
+UNITTEST_HEADER(mDNSCoreReceiveTest)
+ UNITTEST_TEST(InitmDNSCoreReceiveTest)
+ UNITTEST_TEST(ValidQueryReqTest)
+ UNITTEST_TEST(NullDstQueryReqTest)
+ UNITTEST_TEST(ReceiveArpLogMsgTest)
+UNITTEST_FOOTER
+
+UNITTEST_HEADER(InitmDNSCoreReceiveTest)
+ mDNSPlatformTimeInit();
+ init_logging_ut();
+ mDNS_LoggingEnabled = 0;
+ mDNS_PacketLoggingEnabled = 0;
+UNITTEST_FOOTER
+
+UNITTEST_HEADER(ReceiveArpLogMsgTest)
+ // Init unit test environment and verify no error occurred.
+ mStatus result = init_mdns_environment(mDNStrue);
+ UNITTEST_ASSERT(result == mStatus_NoError);
+
+ UNITTEST_ASSERT(result == mStatus_NoError);
+ ArpLogMsgTest(&mDNSStorage, (const ARP_EthIP *) arp_request_packet, primary_interfaceID);
+ UNITTEST_ASSERT(result == mStatus_NoError);
+UNITTEST_FOOTER
+
+UNITTEST_HEADER(ValidQueryReqTest)
+ mDNS *const m = &mDNSStorage;
+ mDNSAddr srcaddr, dstaddr;
+ 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 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
+ UNITTEST_ASSERT(m->mDNSStats.NormalQueries == 1);
+
+UNITTEST_FOOTER
+
+UNITTEST_HEADER(NullDstQueryReqTest)
+
+ 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
+ UNITTEST_ASSERT(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.
+ UNITTEST_ASSERT(m->MPktNum == 1);
+
+UNITTEST_FOOTER
+
+void InitmDNSStorage(mDNS *const m)
+{
+ memset(m, 0, sizeof(mDNS));
+}
+
diff --git a/mDNSResponder/unittests/mDNSCoreReceiveTest.h b/mDNSResponder/unittests/mDNSCoreReceiveTest.h
new file mode 100644
index 00000000..7c7274e9
--- /dev/null
+++ b/mDNSResponder/unittests/mDNSCoreReceiveTest.h
@@ -0,0 +1,9 @@
+
+#ifndef mDNSCoreReceiveTest_h
+#define mDNSCoreReceiveTest_h
+
+#include "unittest.h"
+
+int mDNSCoreReceiveTest(void);
+
+#endif /* mDNSCoreReceiveTest_h */
diff --git a/mDNSResponder/unittests/main.c b/mDNSResponder/unittests/main.c
index 752fffb7..9b30ab4c 100644
--- a/mDNSResponder/unittests/main.c
+++ b/mDNSResponder/unittests/main.c
@@ -18,16 +18,25 @@
#include "unittest.h"
#include "DNSMessageTest.h"
+#include "ResourceRecordTest.h"
+#include "mDNSCoreReceiveTest.h"
+#include "CNameRecordTests.h"
+#include "LocalOnlyTimeoutTests.h"
const char *HWVersionString = "unittestMac1,1";
const char *OSVersionString = "unittest 1.1.1 (1A111)";
const char *BinaryNameString = "unittest";
const char *VersionString = "unittest mDNSResponer-00 (Jan 1 1970 00:00:00)";
-int run_tests(void);
+
UNITTEST_HEADER(run_tests)
UNITTEST_GROUP(DNSMessageTest)
+UNITTEST_GROUP(ResourceRecordTest)
+UNITTEST_GROUP(mDNSCoreReceiveTest)
+//UNITTEST_GROUP(CNameRecordTests) // Commenting out until issue reported in <rdar://problem/30589360> is debugged.
+UNITTEST_GROUP(LocalOnlyTimeoutTests)
UNITTEST_FOOTER
-UNITTEST_MAIN
+// UNITTEST_MAIN is run in daemon.c
+
diff --git a/mDNSResponder/unittests/mdns_macosx_ut.c b/mDNSResponder/unittests/mdns_macosx_ut.c
new file mode 100644
index 00000000..9dae75a0
--- /dev/null
+++ b/mDNSResponder/unittests/mdns_macosx_ut.c
@@ -0,0 +1,66 @@
+#include "DNSCommon.h" // Defines general DNS utility routines
+
+// To match *either* a v4 or v6 instance of this interface
+mDNSlocal mDNSInterfaceID SearchForInterfaceByAddr(mDNSAddr* addr)
+{
+ NetworkInterfaceInfoOSX *i;
+ for (i = mDNSStorage.p->InterfaceList; i; i = i->next)
+ if (i->Exists)
+ {
+ if ((i->ifinfo.ip.type == mDNSAddrType_IPv4) &&
+ i->ifinfo.ip.ip.v4.NotAnInteger == addr->ip.v4.NotAnInteger)
+ return i->ifinfo.InterfaceID;
+ else if ((i->ifinfo.ip.type == mDNSAddrType_IPv6) &&
+ (i->ifinfo.ip.ip.v6.l[0] == addr->ip.v6.l[0] &&
+ i->ifinfo.ip.ip.v6.l[1] == addr->ip.v6.l[1] &&
+ i->ifinfo.ip.ip.v6.l[2] == addr->ip.v6.l[2] &&
+ i->ifinfo.ip.ip.v6.l[3] == addr->ip.v6.l[3])
+ )
+ return i->ifinfo.InterfaceID;
+ }
+ return(NULL);
+}
+
+mDNSexport void SetInterfaces_ut(mDNSInterfaceID* pri_id, mDNSAddr *pri_v4, mDNSAddr* pri_v6, mDNSAddr* pri_router)
+{
+ mDNSs32 utc = mDNSPlatformUTC();
+
+ MarkAllInterfacesInactive(utc);
+ UpdateInterfaceList(utc);
+ ClearInactiveInterfaces(utc);
+ SetupActiveInterfaces(utc);
+
+ // set primary interface info
+ {
+ mDNSAddr* addr;
+ NetworkChangedKey_IPv4 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4);
+ NetworkChangedKey_IPv6 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6);
+ NetworkChangedKey_Hostnames = SCDynamicStoreKeyCreateHostNames(NULL);
+ NetworkChangedKey_Computername = SCDynamicStoreKeyCreateComputerName(NULL);
+ NetworkChangedKey_DNS = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetDNS);
+ NetworkChangedKey_StateInterfacePrefix = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, CFSTR(""), NULL);
+
+ mDNSPlatformGetPrimaryInterface(pri_v4, pri_v6, pri_router);
+ addr = (pri_v4->type == mDNSAddrType_IPv4) ? pri_v4 : pri_v6;
+ *pri_id = SearchForInterfaceByAddr(addr);
+
+ CFRelease(NetworkChangedKey_IPv4);
+ CFRelease(NetworkChangedKey_IPv6);
+ CFRelease(NetworkChangedKey_Hostnames);
+ CFRelease(NetworkChangedKey_Computername);
+ CFRelease(NetworkChangedKey_DNS);
+ CFRelease(NetworkChangedKey_StateInterfacePrefix);
+ }
+}
+
+mDNSexport mDNSBool mDNSMacOSXCreateEtcHostsEntry_ut(const domainname *domain, const struct sockaddr *sa, const domainname *cname, char *ifname, AuthHash *auth)
+{
+ return mDNSMacOSXCreateEtcHostsEntry(domain, sa, cname, ifname, auth);
+}
+
+mDNSexport void UpdateEtcHosts_ut(void *context)
+{
+ mDNS_Lock(&mDNSStorage);
+ UpdateEtcHosts(&mDNSStorage, context);
+ mDNS_Unlock(&mDNSStorage);
+}
diff --git a/mDNSResponder/unittests/mdns_ut.c b/mDNSResponder/unittests/mdns_ut.c
new file mode 100644
index 00000000..907cfa69
--- /dev/null
+++ b/mDNSResponder/unittests/mdns_ut.c
@@ -0,0 +1,17 @@
+#include "DNSCommon.h" // Defines general DNS utility routines
+
+mDNSexport mStatus mDNS_InitStorage_ut(mDNS *const m, mDNS_PlatformSupport *const p,
+ CacheEntity *rrcachestorage, mDNSu32 rrcachesize,
+ mDNSBool AdvertiseLocalAddresses, mDNSCallback *Callback, void *Context)
+{
+ return mDNS_InitStorage(m, p, rrcachestorage, rrcachesize, AdvertiseLocalAddresses, Callback, Context);
+}
+
+mDNSexport mStatus ArpLogMsgTest(mDNS *const m, const ARP_EthIP *const arp, const mDNSInterfaceID InterfaceID)
+{
+ NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID);
+ static const char msg[] = "ARP Req message";
+ LogMsg("Arp %-7s %s %.6a %.4a for %.4a",
+ intf->ifname, msg, arp->sha.b, arp->spa.b, arp->tpa.b);
+ return mStatus_NoError;
+}
diff --git a/mDNSResponder/unittests/uds_daemon_ut.c b/mDNSResponder/unittests/uds_daemon_ut.c
new file mode 100644
index 00000000..8a07e9ba
--- /dev/null
+++ b/mDNSResponder/unittests/uds_daemon_ut.c
@@ -0,0 +1,43 @@
+#include "DNSCommon.h" // Defines general DNS utility routines
+
+mDNSexport mStatus handle_client_request_ut(void *req)
+{
+ return handle_client_request((request_state*)req);
+}
+
+mDNSexport void LogCacheRecords_ut(mDNSs32 now, mDNSu32* retCacheUsed, mDNSu32* retCacheActive)
+{
+ mDNSu32 CacheUsed =0, CacheActive =0, slot;
+ const CacheGroup *cg;
+ const CacheRecord *cr;
+
+ LogMsgNoIdent("------------ Cache -------------");
+ LogMsgNoIdent("Slt Q TTL if U Type rdlen");
+ for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
+ {
+ for (cg = mDNSStorage.rrcache_hash[slot]; cg; cg=cg->next)
+ {
+ CacheUsed++; // Count one cache entity for the CacheGroup object
+ for (cr = cg->members; cr; cr=cr->next)
+ {
+ const mDNSs32 remain = cr->resrec.rroriginalttl - (now - cr->TimeRcvd) / mDNSPlatformOneSecond;
+ const char *ifname;
+ mDNSInterfaceID InterfaceID = cr->resrec.InterfaceID;
+ if (!InterfaceID && cr->resrec.rDNSServer && cr->resrec.rDNSServer->scoped)
+ InterfaceID = cr->resrec.rDNSServer->interface;
+ ifname = InterfaceNameForID(&mDNSStorage, InterfaceID);
+ if (cr->CRActiveQuestion) CacheActive++;
+ PrintOneCacheRecord(cr, slot, remain, ifname, &CacheUsed);
+ PrintCachedRecords(cr, slot, remain, ifname, &CacheUsed);
+ }
+ }
+ }
+
+ *retCacheUsed = CacheUsed;
+ *retCacheActive = CacheActive;
+}
+
+mDNSexport int LogEtcHosts_ut(mDNS *const m)
+{
+ return LogEtcHosts(m);
+}
diff --git a/mDNSResponder/unittests/unittest.h b/mDNSResponder/unittests/unittest.h
index a219aa86..84dd5d77 100644
--- a/mDNSResponder/unittests/unittest.h
+++ b/mDNSResponder/unittests/unittest.h
@@ -20,7 +20,9 @@
#include <unistd.h>
#include <assert.h>
#include <signal.h>
+#include "unittest_common.h"
+#include <MacTypes.h>
#ifndef _UNITTEST_H_
#define _UNITTEST_H_
@@ -38,7 +40,8 @@ typedef struct __test_item_
int iter_count;
} __test_item;
-
+int run_tests(void);
+
#define UNITTEST_HEADER(X) int X() { int __success = 1; __test_item* __i = NULL;
#define UNITTEST_GROUP(X) { printf("== %s ==\n", #X); __success = X() && __success; }
@@ -61,9 +64,14 @@ void _unittest_print_list(__test_item* __i);
signal(SIGPIPE, SIG_IGN); \
FILE* fp; \
unlink("unittest_success"); \
- if (!run_tests()) return -1; \
+ if (!run_tests()) \
+ { \
+ printf("unit test FAILED\n"); \
+ return -1; \
+ } \
fp = fopen("unittest_success", "w"); \
if (!fp) return -2; \
+ fprintf(fp, "unit test %s\n", "SUCCEEDED"); \
fclose(fp); \
printf("unit test SUCCESS\n"); \
if (argc != 1) \
@@ -74,6 +82,38 @@ void _unittest_print_list(__test_item* __i);
} \
return 0; \
}
+#define UNITTEST_SENDDNSMESSAGE mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNSu8 *end, \
+ mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, \
+ mDNSIPPort dstport, TCPSocket *sock, DomainAuthInfo *authInfo, \
+ mDNSBool useBackgroundTrafficClass) \
+ { \
+ (void)(m); \
+ (void)(msg); \
+ (void)(end); \
+ (void)(InterfaceID); \
+ (void)(src); \
+ (void)(dst); \
+ (void)(dstport); \
+ (void)(sock); \
+ (void)(authInfo); \
+ (void)(useBackgroundTrafficClass); \
+ return 0; \
+ }
+
+#define UNITTEST_SETSOCKOPT void mDNSPlatformSetSocktOpt(void *sockCxt, mDNSTransport_Type transType, \
+ mDNSAddr_Type addrType, const DNSQuestion *q) \
+ { \
+ (void)(sockCxt); \
+ (void)(transType); \
+ (void)(addrType); \
+ (void)(q); \
+ return; \
+ }
+
+#define UNITTEST_UDPCLOSE void mDNSPlatformUDPClose(UDPSocket *sock) \
+ { \
+ (void)(sock); \
+ }
#define UNITTEST_FAIL_ASSERT { assert(((void*)__func__) == 0); }
diff --git a/mDNSResponder/unittests/unittest_common.c b/mDNSResponder/unittests/unittest_common.c
new file mode 100644
index 00000000..39718059
--- /dev/null
+++ b/mDNSResponder/unittests/unittest_common.c
@@ -0,0 +1,157 @@
+#include "unittest_common.h"
+#include "dns_sd.h"
+#include "mDNSEmbeddedAPI.h"
+#include "mDNSMacOSX.h"
+
+static mDNS_PlatformSupport PlatformStorage;
+#define RR_CACHE_SIZE ((32*1024) / sizeof(CacheRecord))
+static CacheEntity gRrcachestorage[RR_CACHE_SIZE];
+
+// Primary interface info that is used when simulating the receive of the response packet
+mDNSInterfaceID primary_interfaceID;
+mDNSAddr primary_v4;
+mDNSAddr primary_v6;
+mDNSAddr primary_router;
+
+// This function sets up the minimum environement to run a unit test. It
+// initializes logging, interfaces, and timenow.
+mDNSexport mStatus init_mdns_environment(mDNSBool enableLogging)
+{
+ mDNS *m = &mDNSStorage;
+
+ init_logging_ut();
+ mDNS_LoggingEnabled = enableLogging;
+ mDNS_PacketLoggingEnabled = enableLogging;
+
+ mStatus result = mDNS_InitStorage_ut(m, &PlatformStorage, gRrcachestorage, RR_CACHE_SIZE, mDNSfalse, mDNSNULL, mDNSNULL);
+ if (result != mStatus_NoError)
+ return result;
+
+ primary_v4 = primary_v6 = primary_router = zeroAddr;
+ SetInterfaces_ut(&primary_interfaceID, &primary_v4, &primary_v6, &primary_router);
+
+ m->timenow = mDNS_TimeNow_NoLock(m);
+ return mStatus_NoError;
+}
+
+// This function sets up the minimum environement to run a unit test. It
+// initializes logging and timenow. This is the call to use if your
+// unit test does not use interfaces.
+mDNSexport mStatus init_mdns_storage()
+{
+ mDNS *m = &mDNSStorage;
+
+ init_logging_ut();
+ mDNS_LoggingEnabled = 1;
+ mDNS_PacketLoggingEnabled = 1;
+
+ mStatus result = mDNS_InitStorage_ut(m, &PlatformStorage, gRrcachestorage, RR_CACHE_SIZE, mDNSfalse, mDNSNULL, mDNSNULL);
+ if (result != mStatus_NoError)
+ return result;
+
+ return mStatus_NoError;
+}
+
+mDNSlocal void init_client_request(request_state* req, char *msgbuf, size_t msgSize, uint32_t op)
+{
+ // Simulate read_msg behavior since unit test does not open a socket
+ memset(req, 0, sizeof(request_state));
+
+ req->ts = t_complete;
+ req->msgbuf = mDNSNULL;
+ req->msgptr = msgbuf;
+ req->msgend = msgbuf + msgSize;
+
+ // The rest of the request values are set in order to simulate a request
+ req->sd = client_req_sd;
+ req->uid = client_req_uid;
+ req->hdr_bytes = client_req_hdr_bytes;
+ req->hdr.version = client_req_hdr_version;
+ req->hdr.op = op; // query_request
+ req->hdr.datalen = msgSize;
+ req->data_bytes = msgSize;
+ req->process_id = client_req_process_id;
+ memcpy(req->pid_name, client_req_pid_name, strlen(client_req_pid_name));
+}
+
+// This function calls the mDNSResponder handle_client_request() API. It initializes
+// the request and query data structures.
+mDNSexport mStatus start_client_request(request_state* req, char *msgbuf, size_t msgsz, uint32_t op, UDPSocket* socket)
+{
+ // Process the unit test's client request
+ init_client_request(req, msgbuf, msgsz, op);
+
+ mStatus result = handle_client_request_ut((void*)req);
+ DNSQuestion* q = &req->u.queryrecord.q;
+ q->LocalSocket = socket;
+ return result;
+}
+
+// This function calls the mDNSResponder mDNSCoreReceive() API.
+mDNSexport void receive_response(const request_state* req, DNSMessage *msg, size_t msgSize)
+{
+ mDNS *m = &mDNSStorage;
+ mDNSAddr srcaddr;
+ mDNSIPPort srcport, dstport;
+ const mDNSu8 * end;
+ DNSQuestion *q = (DNSQuestion *)&req->u.queryrecord.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);
+ 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);
+ len += sizeof(mDNSu32); // interface index
+ len += sizeof(DNSServiceErrorType);
+ len += strlen(name) + 1;
+ len += 3 * sizeof(mDNSu16); // type, class, rdlen
+ len += rdlen;
+ len += sizeof(mDNSu32); // TTL
+ return len;
+}
+
+
+void free_req(request_state* req)
+{
+ // Cleanup request's memory usage
+ while (req->replies)
+ {
+ reply_state *reply = req->replies;
+ req->replies = req->replies->next;
+ mDNSPlatformMemFree(reply);
+ }
+ req->replies = NULL;
+ mDNSPlatformMemFree(req);
+}
+
+// Unit test support functions follow
+#define SA_LEN(addr) (((addr)->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in))
+
+mDNSexport void get_ip(const char *const name, struct sockaddr_storage *result)
+{
+ struct addrinfo* aiList;
+ int err = getaddrinfo(name, NULL, NULL, &aiList);
+ if (err) fprintf(stderr, "getaddrinfo error %d for %s", err, name);
+ else memcpy(result, aiList->ai_addr, SA_LEN(aiList->ai_addr));
+ if (aiList) freeaddrinfo(aiList);
+}
+
diff --git a/mDNSResponder/unittests/unittest_common.h b/mDNSResponder/unittests/unittest_common.h
new file mode 100644
index 00000000..a4af2e67
--- /dev/null
+++ b/mDNSResponder/unittests/unittest_common.h
@@ -0,0 +1,57 @@
+#ifndef UNITTEST_COMMON_H
+#define UNITTEST_COMMON_H
+
+#include "dns_sd.h"
+#include "uds_daemon.h"
+#include "uDNS.h"
+#include "dnssd_ipc.h"
+#include <netdb.h> // for getaddrinfo
+#include <net/if.h>
+#include <pthread.h>
+
+// Primary interface info that is used when simulating the receive of the response packet
+extern mDNSInterfaceID primary_interfaceID;
+extern mDNSAddr primary_v4;
+extern mDNSAddr primary_v6;
+extern mDNSAddr primary_router;
+
+// Arbitrary values to simulate a client_request request
+#define client_req_sd 12
+#define client_req_uid 502
+#define client_req_hdr_bytes 28
+#define client_req_hdr_version 1
+#define client_resp_src_port 13568
+#define client_resp_dst_port 49339
+#define uDNS_TargetQID 16745
+#define client_req_process_id 15418
+static char client_req_pid_name[MAXCOMLEN] = "mDNSUnitTest";
+
+//Arbitrary values to simulate a DNS server
+#define dns_server_timeout 30
+#define dns_server_resGroupID 12
+static const mDNSv4Addr dns_server_ipv4 = {{ 192, 168, 1, 20 }};
+
+extern mStatus init_mdns_environment(mDNSBool enableLogging);
+extern mStatus init_mdns_storage(void);
+extern size_t get_reply_len(char* name, uint16_t rdlen);
+extern mStatus start_client_request(request_state* req, char *msgbuf, size_t msgsz, uint32_t op, UDPSocket* socket);
+extern void receive_response(const request_state* req, DNSMessage *msg, size_t msgSize);
+extern void get_ip(const char *const name, struct sockaddr_storage *result);
+extern void free_req(request_state* req);
+
+extern mStatus mDNS_InitStorage_ut(mDNS *const m, mDNS_PlatformSupport *const p,
+ CacheEntity *rrcachestorage, mDNSu32 rrcachesize,
+ mDNSBool AdvertiseLocalAddresses, mDNSCallback *Callback, void *Context);
+extern void init_logging_ut(void);
+extern void SetInterfaces_ut(mDNSInterfaceID* primary_interfaceID, mDNSAddr *primary_v4,
+ mDNSAddr* primary_v6, mDNSAddr* primary_router);
+extern mStatus handle_client_request_ut(void *req);
+extern void LogCacheRecords_ut(mDNSs32 now, mDNSu32* retCacheUsed, mDNSu32* retCacheActive);
+extern int LogEtcHosts_ut(mDNS *const m);
+extern mDNSBool mDNSMacOSXCreateEtcHostsEntry_ut(const domainname *domain, const struct sockaddr *sa,
+ const domainname *cname, char *ifname, AuthHash *auth);
+extern void UpdateEtcHosts_ut(void *context);
+extern mStatus ArpLogMsgTest(mDNS *const m, const ARP_EthIP *const arp, const mDNSInterfaceID InterfaceID);
+
+
+#endif /* UNITTEST_COMMON_H */