diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-09-19 08:56:09 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-09-20 11:23:33 +0200 |
commit | 4c086a244624bf36865edcfa4309c333d7d7200d (patch) | |
tree | e566ffc50a6b6fdb46702ac57c8e7c4088b60b42 /mDNSResponder/unittests | |
parent | mDNSResponder: Update to v765.50.9 (diff) | |
download | rtems-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.c | 400 | ||||
-rw-r--r-- | mDNSResponder/unittests/CNameRecordTests.h | 9 | ||||
-rw-r--r-- | mDNSResponder/unittests/LocalOnlyTimeoutTests.c | 378 | ||||
-rw-r--r-- | mDNSResponder/unittests/LocalOnlyTimeoutTests.h | 9 | ||||
-rw-r--r-- | mDNSResponder/unittests/ResourceRecordTest.c | 5 | ||||
-rw-r--r-- | mDNSResponder/unittests/daemon_ut.c | 35 | ||||
-rw-r--r-- | mDNSResponder/unittests/mDNSCoreReceiveTest.c | 151 | ||||
-rw-r--r-- | mDNSResponder/unittests/mDNSCoreReceiveTest.h | 9 | ||||
-rw-r--r-- | mDNSResponder/unittests/main.c | 13 | ||||
-rw-r--r-- | mDNSResponder/unittests/mdns_macosx_ut.c | 66 | ||||
-rw-r--r-- | mDNSResponder/unittests/mdns_ut.c | 17 | ||||
-rw-r--r-- | mDNSResponder/unittests/uds_daemon_ut.c | 43 | ||||
-rw-r--r-- | mDNSResponder/unittests/unittest.h | 44 | ||||
-rw-r--r-- | mDNSResponder/unittests/unittest_common.c | 157 | ||||
-rw-r--r-- | mDNSResponder/unittests/unittest_common.h | 57 |
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, ¬Used); + 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, ¬Used); + 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 */ |