]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/Tests/LocalOnlyTimeoutTest.m
mDNSResponder-1096.0.2.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / Tests / LocalOnlyTimeoutTest.m
1 /*
2 * Copyright (c) 2017-2019 Apple Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "unittest_common.h"
18 #include "mDNSMacOSX.h"
19 #import <XCTest/XCTest.h>
20
21 // This query request message was generated from the following command: "dns-sd -lo -timeout -Q cardinal2.apple.com. A"
22 char query_req_msgbuf[33]= {
23 0x00, 0x01, 0x90, 0x00,
24 // DNSServiceFlags.L = (kDNSServiceFlagsReturnIntermediates |kDNSServiceFlagsSuppressUnusable | kDNSServiceFlagsTimeout)
25 0xff, 0xff, 0xff, 0xff,
26 // interfaceIndex = mDNSInterface_LocalOnly
27 0x63, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c,
28 0x32, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x00, 0x00, 0x01, 0x00,
29 0x01
30 };
31
32 mDNSlocal mStatus InitEtcHostsRecords(void)
33 {
34 mDNS *m = &mDNSStorage;
35 struct sockaddr_storage hostaddr;
36
37 AuthHash newhosts;
38 mDNSPlatformMemZero(&newhosts, sizeof(AuthHash));
39
40 memset(&hostaddr, 0, sizeof(hostaddr));
41 get_ip("127.0.0.1", &hostaddr);
42
43 domainname domain;
44 MakeDomainNameFromDNSNameString(&domain, "localhost");
45
46 mDNSMacOSXCreateEtcHostsEntry_ut(&domain, (struct sockaddr *) &hostaddr, mDNSNULL, mDNSNULL, &newhosts);
47
48 memset(&hostaddr, 0, sizeof(hostaddr));
49 get_ip("0000:0000:0000:0000:0000:0000:0000:0001", &hostaddr);
50
51 MakeDomainNameFromDNSNameString(&domain, "localhost");
52
53 mDNSMacOSXCreateEtcHostsEntry_ut(&domain, (struct sockaddr *) &hostaddr, mDNSNULL, mDNSNULL, &newhosts);
54
55 memset(&hostaddr, 0, sizeof(hostaddr));
56 get_ip("255.255.255.255", &hostaddr);
57
58 MakeDomainNameFromDNSNameString(&domain, "broadcasthost");
59
60 mDNSMacOSXCreateEtcHostsEntry_ut(&domain, (struct sockaddr *) &hostaddr, mDNSNULL, mDNSNULL, &newhosts);
61
62 memset(&hostaddr, 0, sizeof(hostaddr));
63 get_ip("17.226.40.200", &hostaddr);
64
65 MakeDomainNameFromDNSNameString(&domain, "cardinal2.apple.com");
66
67 mDNSMacOSXCreateEtcHostsEntry_ut(&domain, (struct sockaddr *) &hostaddr, mDNSNULL, mDNSNULL, &newhosts);
68 UpdateEtcHosts_ut(&newhosts);
69
70 m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
71 mDNS_Execute(m);
72
73 return mStatus_NoError;
74 }
75
76 @interface LocalOnlyATimeoutTest : XCTestCase
77 {
78 request_state* client_request_message;
79 UDPSocket* local_socket;
80 char domainname_cstr[MAX_ESCAPED_DOMAIN_NAME];
81 }
82 @end
83
84 @implementation LocalOnlyATimeoutTest
85
86 // The InitUnitTest() initializes a minimal mDNSResponder environment as
87 // well as allocates memory for a local_socket and client request.
88 // It also sets the domainname_cstr specified in the client's query request.
89 // Note: This unit test does not send packets on the wire and it does not open sockets.
90 - (void)setUp
91 {
92 // Init mDNSStorage
93 mStatus result = init_mdns_storage();
94 XCTAssertEqual(result, mStatus_NoError);
95
96 // Allocate a client request
97 local_socket = (UDPSocket *)calloc(1, sizeof(*local_socket));
98
99 // Allocate memory for a request that is used to make client requests.
100 client_request_message = calloc(1, sizeof(request_state));
101
102 // Init domainname that is used by unit tests
103 strlcpy(domainname_cstr, "cardinal2.apple.com.", sizeof(domainname_cstr));
104 }
105
106 // This function does memory cleanup and no verification.
107 - (void)tearDown
108 {
109 mDNSPlatformMemFree(local_socket);
110 }
111
112 // This unit test starts a local only request for "cardinal2.apple.com.". It first
113 // calls start_client_request to start a query, it then verifies the
114 // req and query data structures are set as expected. Next, the cache is verified to
115 // be empty by AnswerNewLocalOnlyQuestion() and so results in GenerateNegativeResponse()
116 // getting called which sets up a reply with a negative answer in it for the client.
117 // On return from mDNS_Execute, the client's reply structure is verified to be set as
118 // expected. Lastly the timeout is simulated and mDNS_Execute is called. This results
119 // in a call to TimeoutQuestions(). And again, the GenerateNegativeResponse() is called
120 // which returns a negative response to the client. This time the client reply is verified
121 // to be setup with a timeout result.
122 - (void)testStartLocalOnlyClientQueryRequest
123 {
124 mDNS *const m = &mDNSStorage;
125 request_state* req = client_request_message;
126 char *msgptr = (char *)query_req_msgbuf;
127 size_t msgsz = sizeof(query_req_msgbuf);
128 DNSQuestion *q;
129 mDNSs32 min_size = sizeof(DNSServiceFlags) + sizeof(mDNSu32) + 4;
130 mStatus err = mStatus_NoError;
131 char qname_cstr[MAX_ESCAPED_DOMAIN_NAME];
132 struct reply_state *reply;
133 size_t len;
134
135 // Process the unit test's client request
136 start_client_request(req, msgptr, msgsz, query_request, local_socket);
137 XCTAssertEqual(err, mStatus_NoError);
138
139 // Verify the query initialized and request fields were set as expected
140 XCTAssertEqual(req->hdr.version, VERSION);
141 XCTAssertGreaterThan((mDNSs32)req->data_bytes, min_size);
142 XCTAssertEqual(req->flags, (kDNSServiceFlagsSuppressUnusable | kDNSServiceFlagsReturnIntermediates | kDNSServiceFlagsTimeout));
143 XCTAssertEqual(req->interfaceIndex, kDNSServiceInterfaceIndexLocalOnly);
144 XCTAssertNotEqual(req->terminate, (req_termination_fn)0);
145
146 q = &req->u.queryrecord.op.q;
147 XCTAssertEqual(q, m->NewLocalOnlyQuestions);
148 XCTAssertNil((__bridge id)m->Questions);
149 XCTAssertNil((__bridge id)m->NewQuestions);
150 XCTAssertEqual(q->SuppressUnusable, 1);
151 XCTAssertEqual(q->ReturnIntermed, 1);
152 XCTAssertEqual(q->Suppressed, mDNSfalse); // Regress <rdar://problem/27571734>
153
154 ConvertDomainNameToCString(&q->qname, qname_cstr);
155 XCTAssertFalse(strcmp(qname_cstr, domainname_cstr));
156 XCTAssertEqual(q->qnamehash, DomainNameHashValue(&q->qname));
157
158 XCTAssertEqual(q->InterfaceID, mDNSInterface_LocalOnly);
159 XCTAssertEqual(q->flags, req->flags);
160 XCTAssertEqual(q->qtype, 1);
161 XCTAssertEqual(q->qclass, 1);
162 XCTAssertEqual(q->LongLived, 0);
163 XCTAssertEqual(q->ExpectUnique, mDNSfalse);
164 XCTAssertEqual(q->ForceMCast, 0);
165 XCTAssertEqual(q->TimeoutQuestion, 1);
166 XCTAssertEqual(q->WakeOnResolve, 0);
167 XCTAssertEqual(q->UseBackgroundTraffic, 0);
168 XCTAssertEqual(q->ValidationRequired, 0);
169 XCTAssertEqual(q->ValidatingResponse, 0);
170 XCTAssertEqual(q->ProxyQuestion, 0);
171 XCTAssertNotEqual((void*)q->QuestionCallback, (void*)mDNSNULL);
172 XCTAssertNil((__bridge id)q->DNSSECAuthInfo);
173 XCTAssertNil((__bridge id)(void*)q->DAIFreeCallback);
174 XCTAssertNotEqual(q->StopTime, 0);
175 XCTAssertEqual(q->AppendSearchDomains, 0);
176 XCTAssertNil((__bridge id)q->DuplicateOf);
177
178 // At this point the the cache is empty. Calling mDNS_Execute will answer the local-only
179 // question with a negative response.
180 m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
181 mDNS_Execute(m); // Regress <rdar://problem/28721294>
182
183 // Verify reply is a negative response and error code is set to kDNSServiceErr_NoSuchRecord error.
184 reply = req->replies;
185 XCTAssertNotEqual(reply, (reply_state*)mDNSNULL);
186
187 XCTAssertNil((__bridge id)m->NewLocalOnlyQuestions);
188 XCTAssertEqual(q->LOAddressAnswers, 0);
189
190 len = get_reply_len(qname_cstr, 0);
191
192 XCTAssertNil((__bridge id)reply->next);
193 XCTAssertEqual(reply->totallen, reply->mhdr->datalen + sizeof(ipc_msg_hdr));
194 XCTAssertEqual(reply->mhdr->version, VERSION);
195 XCTAssertEqual(reply->mhdr->datalen, len);
196 XCTAssertEqual(reply->mhdr->ipc_flags, 0);
197 XCTAssertEqual(reply->mhdr->op, query_reply_op);
198
199 XCTAssertTrue((reply->rhdr->flags & htonl(kDNSServiceFlagsAdd)));
200 XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexLocalOnly); // Regress <rdar://problem/27340874>
201 XCTAssertEqual(reply->rhdr->error,
202 (DNSServiceErrorType)htonl(kDNSServiceErr_NoSuchRecord)); // Regress <rdar://problem/24827555>
203
204 // Simulate what udsserver_idle normally does for clean up
205 freeL("StartLocalOnlyClientQueryRequest:reply", reply);
206 req->replies = NULL;
207
208 // Simulate the query time out of the local-only question.
209 // The expected behavior is a negative answer with time out error
210 m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
211 q->StopTime = mDNS_TimeNow_NoLock(m);
212 m->NextScheduledStopTime -= mDNSPlatformOneSecond*5;
213 mDNS_Execute(m);
214
215 // Verify the reply is a negative response with timeout error.
216 reply = req->replies;
217 XCTAssertNotEqual(reply, (reply_state*)mDNSNULL);
218 XCTAssertNil((__bridge id)m->NewLocalOnlyQuestions);
219 XCTAssertEqual(q->LOAddressAnswers, 0);
220
221 len = get_reply_len(qname_cstr, 0);
222
223 XCTAssertNil((__bridge id)reply->next);
224 XCTAssertEqual(reply->totallen, len + sizeof(ipc_msg_hdr));
225 XCTAssertEqual(reply->mhdr->version, VERSION);
226 XCTAssertEqual(reply->mhdr->datalen, len);
227 XCTAssertEqual(reply->mhdr->ipc_flags, 0);
228 XCTAssertEqual(reply->mhdr->op, query_reply_op);
229 XCTAssertTrue((reply->rhdr->flags & htonl(kDNSServiceFlagsAdd)));
230 XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexLocalOnly); // Regress <rdar://problem/27340874>
231 XCTAssertEqual(reply->rhdr->error,
232 (DNSServiceErrorType)htonl(kDNSServiceErr_Timeout)); // Regress <rdar://problem/27562965>
233
234 // Free request and reallocate to use when query is restarted
235 free_req(req);
236 client_request_message = calloc(1, sizeof(request_state));
237 }
238
239 // This unit test populates the cache with four /etc/hosts records and then
240 // verifies there are four entries in the cache.
241 - (void)testPopulateCacheWithClientLOResponseRecords
242 {
243 mDNS *const m = &mDNSStorage;
244
245 // Verify cache is empty
246 int count = LogEtcHosts_ut(m);
247 XCTAssertEqual(count, 0);
248
249 // Populate /etc/hosts
250 mStatus result = InitEtcHostsRecords();
251 XCTAssertEqual(result, mStatus_NoError);
252
253 // mDNS_Execute is called to populate the /etc/hosts cache.
254 m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
255 mDNS_Execute(m);
256
257 count = LogEtcHosts_ut(m);
258 XCTAssertEqual(count, 4);
259
260 [self _testRestartLocalOnlyClientQueryRequest]; // Continuation of this test
261 }
262
263 // This unit test starts a local only request for "cardinal2.apple.com.". It first
264 // calls start_client_request to start a query, it then verifies the
265 // req and query data structures are set as expected. Next, the cache is verified to
266 // contain the answer by AnswerNewLocalOnlyQuestion() and so results in setting up an
267 // answer reply to the client. On return from mDNS_Execute, the client's reply structure
268 // is verified to be set as expected. Lastly the timeout is simulated and mDNS_Execute is
269 // called. This results in a call to TimeoutQuestions(). And this time, the
270 // GenerateNegativeResponse() is called which returns a negative response to the client
271 // which specifies the timeout occurred. Again, the answer reply is verified to
272 // to specify a timeout.
273 - (void)_testRestartLocalOnlyClientQueryRequest
274 {
275 mDNS *const m = &mDNSStorage;
276 request_state* req = client_request_message;
277 char *msgptr = (char *)query_req_msgbuf;
278 size_t msgsz = sizeof(query_req_msgbuf); DNSQuestion *q;
279 mDNSs32 min_size = sizeof(DNSServiceFlags) + sizeof(mDNSu32) + 4;
280 mStatus err = mStatus_NoError;
281 char qname_cstr[MAX_ESCAPED_DOMAIN_NAME];
282 struct reply_state *reply;
283 size_t len;
284
285 // Process the unit test's client request
286 start_client_request(req, msgptr, msgsz, query_request, local_socket);
287 XCTAssertEqual(err, mStatus_NoError);
288
289 XCTAssertEqual(req->hdr.version, VERSION);
290 XCTAssertGreaterThan((mDNSs32)req->data_bytes, min_size);
291 XCTAssertEqual(req->flags, (kDNSServiceFlagsSuppressUnusable | kDNSServiceFlagsReturnIntermediates | kDNSServiceFlagsTimeout));
292 XCTAssertEqual(req->interfaceIndex, kDNSServiceInterfaceIndexLocalOnly);
293 XCTAssertNotEqual(req->terminate, (req_termination_fn)0);
294 XCTAssertNil((__bridge id)m->Questions);
295
296 q = &req->u.queryrecord.op.q;
297 XCTAssertEqual(q, m->NewLocalOnlyQuestions);
298 XCTAssertEqual(q->SuppressUnusable, 1);
299 XCTAssertEqual(q->ReturnIntermed, 1);
300 XCTAssertEqual(q->Suppressed, mDNSfalse); // Regress <rdar://problem/27571734>
301 XCTAssertEqual(q->qnamehash, DomainNameHashValue(&q->qname));
302 XCTAssertEqual(q->InterfaceID, mDNSInterface_LocalOnly);
303 XCTAssertEqual(q->flags, req->flags);
304 XCTAssertEqual(q->qtype, 1);
305 XCTAssertEqual(q->qclass, 1);
306 XCTAssertEqual(q->LongLived, 0);
307 XCTAssertEqual(q->ExpectUnique, mDNSfalse);
308 XCTAssertEqual(q->ForceMCast, 0);
309 XCTAssertEqual(q->TimeoutQuestion, 1);
310 XCTAssertEqual(q->WakeOnResolve, 0);
311 XCTAssertEqual(q->UseBackgroundTraffic, 0);
312 XCTAssertEqual(q->ValidationRequired, 0);
313 XCTAssertEqual(q->ValidatingResponse, 0);
314 XCTAssertEqual(q->ProxyQuestion, 0);
315 XCTAssertNotEqual((void*)q->QuestionCallback, (void*)mDNSNULL);
316 XCTAssertNil((__bridge id)q->DNSSECAuthInfo);
317 XCTAssertNil((__bridge id)(void*)q->DAIFreeCallback);
318 XCTAssertNotEqual(q->StopTime, 0);
319 XCTAssertEqual(q->AppendSearchDomains, 0);
320 XCTAssertNil((__bridge id)q->DuplicateOf);
321 ConvertDomainNameToCString(&q->qname, qname_cstr);
322 XCTAssertFalse(strcmp(qname_cstr, domainname_cstr));
323
324 // Answer local-only question with found cache entry
325 m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
326 mDNS_Execute(m); // Regress <rdar://problem/28721294>
327 XCTAssertNil((__bridge id)m->NewLocalOnlyQuestions);
328 XCTAssertEqual(req->u.queryrecord.op.answered, 1);
329 XCTAssertEqual(q->LOAddressAnswers, 1);
330 XCTAssertEqual(q, m->LocalOnlyQuestions);
331
332 reply = req->replies;
333 len = get_reply_len(qname_cstr, 4);
334
335 XCTAssertNil((__bridge id)reply->next);
336 XCTAssertEqual(reply->totallen, len + sizeof(ipc_msg_hdr));
337 XCTAssertEqual(reply->mhdr->version, VERSION);
338 XCTAssertEqual(reply->mhdr->datalen, len);
339 XCTAssertEqual(reply->mhdr->ipc_flags, 0);
340 XCTAssertEqual(reply->mhdr->op, query_reply_op);
341 XCTAssertTrue((reply->rhdr->flags & htonl(kDNSServiceFlagsAdd)));
342 XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexLocalOnly); // Regress <rdar://problem/27340874>
343 XCTAssertEqual(reply->rhdr->error, kDNSServiceErr_NoError);
344
345 // Simulate the query time out of the local-only question.
346 // The expected behavior is a negative answer with time out error
347 m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
348 q->StopTime = mDNS_TimeNow_NoLock(m);
349 m->NextScheduledStopTime -= mDNSPlatformOneSecond*5;
350 mDNS_Execute(m);
351
352 reply = req->replies->next;
353 XCTAssertNotEqual(reply, (reply_state*)mDNSNULL);
354 XCTAssertNil((__bridge id)reply->next);
355 XCTAssertNil((__bridge id)m->NewLocalOnlyQuestions);
356 XCTAssertEqual(q->LOAddressAnswers, 0);
357 len = get_reply_len(qname_cstr, 0);
358
359 XCTAssertNil((__bridge id)reply->next);
360 XCTAssertEqual(reply->totallen, len + + sizeof(ipc_msg_hdr));
361 XCTAssertEqual(reply->mhdr->version, VERSION);
362 XCTAssertEqual(reply->mhdr->datalen, len);
363 XCTAssertEqual(reply->mhdr->ipc_flags, 0);
364 XCTAssertEqual(reply->mhdr->op, query_reply_op);
365 XCTAssertTrue((reply->rhdr->flags & htonl(kDNSServiceFlagsAdd)));
366 XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexLocalOnly); // Regress <rdar://problem/27340874>
367 XCTAssertEqual(reply->rhdr->error,
368 (DNSServiceErrorType)htonl(kDNSServiceErr_Timeout)); // Regress <rdar://problem/27562965>
369
370 free_req(req);
371 }
372
373 @end