Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / inet / tests / TestInetLayerDNS.cpp
1 /*
2  *
3  *    Copyright (c) 2020 Project CHIP Authors
4  *    Copyright (c) 2013-2017 Nest Labs, Inc.
5  *    All rights reserved.
6  *
7  *    Licensed under the Apache License, Version 2.0 (the "License");
8  *    you may not use this file except in compliance with the License.
9  *    You may obtain a copy of the License at
10  *
11  *        http://www.apache.org/licenses/LICENSE-2.0
12  *
13  *    Unless required by applicable law or agreed to in writing, software
14  *    distributed under the License is distributed on an "AS IS" BASIS,
15  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  *    See the License for the specific language governing permissions and
17  *    limitations under the License.
18  */
19
20 /**
21  *    @file
22  *      This file tests DNS resolution using the CHIP Inet Layer APIs.
23  *
24  */
25
26 #ifndef __STDC_LIMIT_MACROS
27 #define __STDC_LIMIT_MACROS
28 #endif
29
30 #include <inttypes.h>
31 #include <stdint.h>
32 #include <string.h>
33 #include <unistd.h>
34
35 #include <nlunit-test.h>
36
37 #include <CHIPVersion.h>
38
39 #include <inet/InetLayer.h>
40 #include <support/CodeUtils.h>
41 #include <support/UnitTestRegistration.h>
42
43 #include <system/SystemClock.h>
44 #include <system/SystemTimer.h>
45
46 #include "TestInetCommon.h"
47 #include "TestSetupSignalling.h"
48
49 using namespace chip;
50 using namespace chip::Inet;
51
52 #if INET_CONFIG_ENABLE_DNS_RESOLVER
53
54 #define TOOL_NAME "TestInetLayerDNS"
55
56 #define DISABLE_BROKEN_DNS_TESTS 1 // https://github.com/project-chip/connectedhomeip/issues/4670
57
58 #define DEFAULT_TEST_DURATION_MILLISECS (20000)
59 #define DEFAULT_CANCEL_TEST_DURATION_MILLISECS (2000)
60
61 static uint32_t sNumResInProgress = 0;
62 constexpr uint8_t kMaxResults     = 20;
63
64 struct DNSResolutionTestCase
65 {
66     const char * hostName;
67     uint8_t dnsOptions;
68     uint8_t maxResults;
69     INET_ERROR expectErr;
70     bool expectIPv4Addrs;
71     bool expectIPv6Addrs;
72 };
73
74 struct DNSResolutionTestContext
75 {
76     nlTestSuite * testSuite;
77     DNSResolutionTestCase testCase;
78     bool callbackCalled;
79     IPAddress resultsBuf[kMaxResults];
80 };
81
82 static void RunTestCase(nlTestSuite * testSuite, const DNSResolutionTestCase & testCase);
83 static void StartTestCase(DNSResolutionTestContext & testContext);
84 static void HandleResolutionComplete(void * appState, INET_ERROR err, uint8_t addrCount, IPAddress * addrArray);
85 static void ServiceNetworkUntilDone(uint32_t timeoutMS);
86 static void HandleSIGUSR1(int sig);
87
88 /**
89  * Test basic name resolution functionality.
90  */
91 static void TestDNSResolution_Basic(nlTestSuite * testSuite, void * testContext)
92 {
93     // clang-format off
94
95 #ifndef DISABLE_BROKEN_DNS_TESTS
96     // Test resolving a name with only IPv4 addresses.
97     RunTestCase(testSuite,
98         DNSResolutionTestCase
99         {
100             "ipv4.google.com",
101             kDNSOption_Default,
102             kMaxResults,
103             INET_NO_ERROR,
104             true,
105             false
106         }
107     );
108
109     // Test resolving a name with only IPv6 addresses.
110     RunTestCase(testSuite,
111         DNSResolutionTestCase
112         {
113             "ipv6.google.com",
114             kDNSOption_Default,
115             kMaxResults,
116             INET_NO_ERROR,
117             false,
118             true
119         }
120     );
121
122     // Test resolving a name with IPv4 and IPv6 addresses.
123     RunTestCase(testSuite,
124         DNSResolutionTestCase
125         {
126             "google.com",
127             kDNSOption_Default,
128             kMaxResults,
129             INET_NO_ERROR,
130             true,
131             true
132         }
133     );
134 #endif
135     // clang-format on
136 }
137
138 /**
139  * Test resolving a name using various address type options.
140  */
141 static void TestDNSResolution_AddressTypeOption(nlTestSuite * testSuite, void * testContext)
142 {
143     // clang-format off
144 #ifndef DISABLE_BROKEN_DNS_TESTS
145
146     // Test requesting IPv4 addresses only.
147 #if INET_CONFIG_ENABLE_IPV4
148     RunTestCase(testSuite,
149         DNSResolutionTestCase
150         {
151             "google.com",
152             kDNSOption_AddrFamily_IPv4Only,
153             kMaxResults,
154             INET_NO_ERROR,
155             true,
156             false
157         }
158     );
159 #endif // INET_CONFIG_ENABLE_IPV4
160
161     // Test requesting IPv6 addresses only.
162     RunTestCase(testSuite,
163         DNSResolutionTestCase
164         {
165             "google.com",
166             kDNSOption_AddrFamily_IPv6Only,
167             kMaxResults,
168             INET_NO_ERROR,
169             false,
170             true
171         }
172     );
173
174     // Test requesting IPv4 address preferentially.
175 #if INET_CONFIG_ENABLE_IPV4
176     RunTestCase(testSuite,
177         DNSResolutionTestCase
178         {
179             "google.com",
180             kDNSOption_AddrFamily_IPv4Preferred,
181             kMaxResults,
182             INET_NO_ERROR,
183             true,
184             true
185         }
186     );
187 #endif // INET_CONFIG_ENABLE_IPV4
188
189     // Test requesting IPv6 address preferentially.
190     RunTestCase(testSuite,
191         DNSResolutionTestCase
192         {
193             "google.com",
194             kDNSOption_AddrFamily_IPv6Preferred,
195             kMaxResults,
196             INET_NO_ERROR,
197             true,
198             true
199         }
200     );
201
202 #endif
203     // clang-format on
204 }
205
206 /**
207  * Test resolving a name with a limited number of results.
208  */
209 static void TestDNSResolution_RestrictedResults(nlTestSuite * testSuite, void * testContext)
210 {
211     // clang-format off
212
213     // Test requesting 2 IPv4 addresses.  This should result in, at most, 2 IPv4 addresses.
214 #if INET_CONFIG_ENABLE_IPV4
215     RunTestCase(testSuite,
216         DNSResolutionTestCase
217         {
218             "google.com",
219             kDNSOption_AddrFamily_IPv4Only,
220             2,
221             INET_NO_ERROR,
222             true,
223             false
224         }
225     );
226 #endif // INET_CONFIG_ENABLE_IPV4
227
228     // Test requesting 2 IPv6 addresses.  This should result in, at most, 2 IPv6 addresses.
229     RunTestCase(testSuite,
230         DNSResolutionTestCase
231         {
232             "google.com",
233             kDNSOption_AddrFamily_IPv6Only,
234             2,
235             INET_NO_ERROR,
236             false,
237             true
238         }
239     );
240
241     // Test requesting 2 addresses, preferring IPv4.  This should result in 1 IPv4 address
242     // followed by 1 IPv6 address.
243 #if INET_CONFIG_ENABLE_IPV4
244     RunTestCase(testSuite,
245         DNSResolutionTestCase
246         {
247             "google.com",
248             kDNSOption_AddrFamily_IPv4Preferred,
249             2,
250             INET_NO_ERROR,
251             true,
252             true
253         }
254     );
255 #endif // INET_CONFIG_ENABLE_IPV4
256
257     // Test requesting 2 addresses, preferring IPv6.  This should result in 1 IPv6 address
258     // followed by 1 IPv4 address.
259     RunTestCase(testSuite,
260         DNSResolutionTestCase
261         {
262             "google.com",
263             kDNSOption_AddrFamily_IPv6Preferred,
264             2,
265             INET_NO_ERROR,
266             true,
267             true
268         }
269     );
270     // clang-format on
271 }
272
273 /**
274  * Test resolving a non-existant name.
275  */
276 static void TestDNSResolution_NoRecord(nlTestSuite * testSuite, void * testContext)
277 {
278     // clang-format off
279 #ifndef DISABLE_BROKEN_DNS_TESTS
280     RunTestCase(testSuite,
281         DNSResolutionTestCase
282         {
283             "www.google.invalid.",
284             kDNSOption_AddrFamily_Any,
285             1,
286             INET_ERROR_HOST_NOT_FOUND,
287             false,
288             false
289         }
290     );
291 #endif
292     // clang-format on
293 }
294
295 /**
296  * Test resolving a name where the resultant DNS entry lacks an A or AAAA record.
297  */
298 static void TestDNSResolution_NoHostRecord(nlTestSuite * testSuite, void * testContext)
299 {
300     // clang-format off
301
302     // Test resolving a name that has no host records (A or AAAA).
303     RunTestCase(testSuite,
304         DNSResolutionTestCase
305         {
306             "_spf.google.com",
307             kDNSOption_AddrFamily_Any,
308             kMaxResults,
309             INET_ERROR_HOST_NOT_FOUND,
310             false,
311             false
312         }
313     );
314
315     // Test resolving a name that has only AAAA records, while requesting IPv4 addresses only.
316 #if INET_CONFIG_ENABLE_IPV4
317     RunTestCase(testSuite,
318         DNSResolutionTestCase
319         {
320             "ipv6.google.com",
321             kDNSOption_AddrFamily_IPv4Only,
322             kMaxResults,
323             INET_ERROR_HOST_NOT_FOUND,
324             true,
325             false
326         }
327     );
328 #endif // INET_CONFIG_ENABLE_IPV4
329
330     // Test resolving a name that has only A records, while requesting IPv6 addresses only.
331     RunTestCase(testSuite,
332         DNSResolutionTestCase
333         {
334             "ipv4.google.com",
335             kDNSOption_AddrFamily_IPv6Only,
336             kMaxResults,
337             INET_ERROR_HOST_NOT_FOUND,
338             false,
339             false
340         }
341     );
342     // clang-format on
343 }
344
345 /**
346  * Test resolving text form IP addresses.
347  */
348 static void TestDNSResolution_TextForm(nlTestSuite * testSuite, void * testContext)
349 {
350     // clang-format off
351     RunTestCase(testSuite,
352         DNSResolutionTestCase
353         {
354             "216.58.194.174",
355             kDNSOption_AddrFamily_Any,
356             1,
357             INET_NO_ERROR,
358             true,
359             false
360         }
361     );
362
363     RunTestCase(testSuite,
364         DNSResolutionTestCase
365         {
366             "2607:f8b0:4005:804::200e",
367             kDNSOption_AddrFamily_Any,
368             1,
369             INET_NO_ERROR,
370             false,
371             true
372         }
373     );
374
375     // Test resolving text form IPv4 and IPv6 addresses while requesting an
376     // incompatible address type.
377
378     RunTestCase(testSuite,
379         DNSResolutionTestCase
380         {
381             "216.58.194.174",
382             kDNSOption_AddrFamily_IPv6Only,
383             1,
384             INET_ERROR_INCOMPATIBLE_IP_ADDRESS_TYPE,
385             false,
386             false
387         }
388     );
389
390     RunTestCase(testSuite,
391         DNSResolutionTestCase
392         {
393             "2607:f8b0:4005:804::200e",
394             kDNSOption_AddrFamily_IPv4Only,
395             1,
396             INET_ERROR_INCOMPATIBLE_IP_ADDRESS_TYPE,
397             false,
398             false
399         }
400     );
401     // clang-format on
402 }
403
404 static void TestDNSResolution_Cancel(nlTestSuite * testSuite, void * inContext)
405 {
406     DNSResolutionTestContext testContext{
407         testSuite, DNSResolutionTestCase{ "www.google.com", kDNSOption_Default, kMaxResults, INET_NO_ERROR, true, false }
408     };
409
410     // Start DNS resolution.
411     StartTestCase(testContext);
412
413     // If address resolution did NOT complete synchronously...
414     // (NOTE: If address resolution completes synchronously then this test is effectively
415     // void, as there's no opportunity to cancel the request).
416     if (!testContext.callbackCalled)
417     {
418         // Cancel the resolution before it completes.
419         gInet.CancelResolveHostAddress(HandleResolutionComplete, &testContext);
420
421         // Service the network for awhile to see what happens (should timeout).
422         ServiceNetworkUntilDone(DEFAULT_CANCEL_TEST_DURATION_MILLISECS);
423
424         // Verify that the completion function was NOT called.
425         NL_TEST_ASSERT(testSuite, testContext.callbackCalled == false);
426     }
427
428     gDone             = true;
429     sNumResInProgress = 0;
430 }
431
432 static void TestDNSResolution_Simultaneous(nlTestSuite * testSuite, void * inContext)
433 {
434     // clang-format off
435     DNSResolutionTestContext tests[] =
436     {
437         {
438             testSuite,
439             DNSResolutionTestCase
440             {
441                 "www.nest.com",
442                 kDNSOption_Default,
443                 kMaxResults,
444                 INET_NO_ERROR,
445                 true,
446                 false
447             }
448         },
449         {
450             testSuite,
451             DNSResolutionTestCase
452             {
453                 "10.0.0.1",
454                 kDNSOption_Default,
455                 kMaxResults,
456                 INET_NO_ERROR,
457                 true,
458                 false
459             }
460         },
461         {
462             testSuite,
463             DNSResolutionTestCase
464             {
465                 "www.google.com",
466                 kDNSOption_Default,
467                 kMaxResults,
468                 INET_NO_ERROR,
469                 true,
470                 true
471             }
472         },
473         {
474             testSuite,
475             DNSResolutionTestCase
476             {
477                 "pool.ntp.org",
478                 kDNSOption_Default,
479                 kMaxResults,
480                 INET_NO_ERROR,
481                 true,
482                 false
483             }
484         }
485     };
486     // clang-format on
487
488     // Start multiple DNS resolutions simultaneously.
489     for (DNSResolutionTestContext & testContext : tests)
490     {
491         StartTestCase(testContext);
492     }
493
494     // Service the network until each completes, or a timeout occurs.
495     ServiceNetworkUntilDone(DEFAULT_TEST_DURATION_MILLISECS);
496
497     // Verify no timeout occurred.
498     NL_TEST_ASSERT(testSuite, gDone == true);
499
500     // Sanity check test logic.
501     NL_TEST_ASSERT(testSuite, sNumResInProgress == 0);
502 }
503
504 static void RunTestCase(nlTestSuite * testSuite, const DNSResolutionTestCase & testCase)
505 {
506     DNSResolutionTestContext testContext{ testSuite, testCase };
507
508     // Start DNS resolution.
509     StartTestCase(testContext);
510
511     // Service the network until the completion callback is called.
512     ServiceNetworkUntilDone(DEFAULT_TEST_DURATION_MILLISECS);
513
514     // Verify no timeout occurred.
515     NL_TEST_ASSERT(testSuite, gDone == true);
516
517     // Sanity check test logic.
518     NL_TEST_ASSERT(testSuite, sNumResInProgress == 0);
519 }
520
521 static void StartTestCase(DNSResolutionTestContext & testContext)
522 {
523     INET_ERROR err                   = INET_NO_ERROR;
524     DNSResolutionTestCase & testCase = testContext.testCase;
525     nlTestSuite * testSuite          = testContext.testSuite;
526
527     gDone = false;
528     sNumResInProgress++;
529
530     printf("Resolving hostname %s\n", testCase.hostName);
531     err = gInet.ResolveHostAddress(testCase.hostName, strlen(testCase.hostName), testCase.dnsOptions, testCase.maxResults,
532                                    testContext.resultsBuf, HandleResolutionComplete, &testContext);
533
534     if (err != INET_NO_ERROR)
535     {
536         printf("ResolveHostAddress failed for %s: %s\n", testCase.hostName, ::chip::ErrorStr(err));
537
538         // Verify the expected error
539         NL_TEST_ASSERT(testSuite, err == testCase.expectErr);
540
541         // Verify the callback WASN'T called
542         NL_TEST_ASSERT(testSuite, testContext.callbackCalled == false); //
543
544         sNumResInProgress--;
545         if (sNumResInProgress == 0)
546         {
547             gDone = true;
548         }
549     }
550 }
551
552 static void HandleResolutionComplete(void * appState, INET_ERROR err, uint8_t addrCount, IPAddress * addrArray)
553 {
554     DNSResolutionTestContext & testContext = *static_cast<DNSResolutionTestContext *>(appState);
555     DNSResolutionTestCase & testCase       = testContext.testCase;
556     nlTestSuite * testSuite                = testContext.testSuite;
557
558     if (err == INET_NO_ERROR)
559     {
560         printf("DNS resolution complete for %s: %" PRIu8 " result%s returned\n", testCase.hostName, addrCount,
561                (addrCount != 1) ? "s" : "");
562         for (uint8_t i = 0; i < addrCount; i++)
563         {
564             char ipAddrStr[INET6_ADDRSTRLEN];
565
566             printf("    %s\n", addrArray[i].ToString(ipAddrStr, sizeof(ipAddrStr)));
567         }
568     }
569     else
570     {
571         printf("DNS resolution complete for %s: %s\n", testCase.hostName, ::chip::ErrorStr(err));
572     }
573
574     // Verify the expected result.
575     NL_TEST_ASSERT(testSuite, err == testCase.expectErr);
576
577     if (err == INET_NO_ERROR)
578     {
579         // Make sure the number of addresses is within the max expected.
580         NL_TEST_ASSERT(testSuite, addrCount <= testCase.maxResults);
581
582         // Determine the types of addresses in the response and their relative ordering.
583         bool respContainsIPv4Addrs = false;
584         bool respContainsIPv6Addrs = false;
585
586         for (uint8_t i = 0; i < addrCount; i++)
587         {
588             respContainsIPv4Addrs = respContainsIPv4Addrs || (addrArray[i].Type() == kIPAddressType_IPv4);
589             respContainsIPv6Addrs = respContainsIPv6Addrs || (addrArray[i].Type() == kIPAddressType_IPv6);
590         }
591
592         // Verify the expected address types were returned.
593         // The current LwIP DNS implementation returns at most one address.  So if the test expects
594         // both IPv4 and IPv6 addresses, relax this to accept either.
595 #if CHIP_SYSTEM_CONFIG_USE_LWIP
596         if (testCase.expectIPv4Addrs && testCase.expectIPv6Addrs)
597         {
598             NL_TEST_ASSERT(testSuite, respContainsIPv4Addrs || respContainsIPv6Addrs);
599         }
600         else
601 #endif // CHIP_SYSTEM_CONFIG_USE_LWIP
602         {
603             if (testCase.expectIPv4Addrs)
604             {
605                 NL_TEST_ASSERT(testSuite, respContainsIPv4Addrs);
606             }
607             if (testCase.expectIPv6Addrs)
608             {
609                 NL_TEST_ASSERT(testSuite, respContainsIPv6Addrs);
610             }
611         }
612
613         // Verify that only the requested address types were returned, and that the
614         // addresses were returned in the correct order.
615         switch (testCase.dnsOptions & kDNSOption_AddrFamily_Mask)
616         {
617         case kDNSOption_AddrFamily_Any:
618             break;
619         case kDNSOption_AddrFamily_IPv4Only:
620             NL_TEST_ASSERT(testSuite, !respContainsIPv6Addrs);
621             break;
622         case kDNSOption_AddrFamily_IPv4Preferred:
623             if (respContainsIPv4Addrs)
624             {
625                 NL_TEST_ASSERT(testSuite, addrArray[0].Type() == kIPAddressType_IPv4);
626             }
627             break;
628         case kDNSOption_AddrFamily_IPv6Only:
629             NL_TEST_ASSERT(testSuite, !respContainsIPv4Addrs);
630             break;
631         case kDNSOption_AddrFamily_IPv6Preferred:
632             if (respContainsIPv6Addrs)
633             {
634                 NL_TEST_ASSERT(testSuite, addrArray[0].Type() == kIPAddressType_IPv6);
635             }
636             break;
637         default:
638             constexpr bool UnexpectedAddressTypeValue = true;
639             NL_TEST_ASSERT(testSuite, !UnexpectedAddressTypeValue);
640         }
641     }
642
643     testContext.callbackCalled = true;
644
645     sNumResInProgress--;
646     if (sNumResInProgress == 0)
647     {
648         gDone = true;
649     }
650 }
651
652 static void ServiceNetworkUntilDone(uint32_t timeoutMS)
653 {
654     uint64_t timeoutTimeMS = System::Layer::GetClock_MonotonicMS() + timeoutMS;
655     struct timeval sleepTime;
656     sleepTime.tv_sec  = 0;
657     sleepTime.tv_usec = 10000;
658
659     while (!gDone)
660     {
661         ServiceNetwork(sleepTime);
662
663         if (System::Layer::GetClock_MonotonicMS() >= timeoutTimeMS)
664         {
665             break;
666         }
667     }
668 }
669
670 static void HandleSIGUSR1(int sig)
671 {
672     gInet.Shutdown();
673
674     exit(0);
675 }
676
677 int TestInetLayerDNSInternal()
678 {
679     // clang-format off
680     const nlTest DNSTests[] =
681     {
682         NL_TEST_DEF("TestDNSResolution:Basic",             TestDNSResolution_Basic),
683         NL_TEST_DEF("TestDNSResolution:AddressTypeOption", TestDNSResolution_AddressTypeOption),
684         NL_TEST_DEF("TestDNSResolution:RestrictedResults", TestDNSResolution_RestrictedResults),
685         NL_TEST_DEF("TestDNSResolution:TextForm",          TestDNSResolution_TextForm),
686         NL_TEST_DEF("TestDNSResolution:NoRecord",          TestDNSResolution_NoRecord),
687         NL_TEST_DEF("TestDNSResolution:NoHostRecord",      TestDNSResolution_NoHostRecord),
688         NL_TEST_DEF("TestDNSResolution:Cancel",            TestDNSResolution_Cancel),
689         NL_TEST_DEF("TestDNSResolution:Simultaneous",      TestDNSResolution_Simultaneous),
690         NL_TEST_SENTINEL() };
691
692     nlTestSuite DNSTestSuite =
693     {
694         "DNS",
695         &DNSTests[0],
696         nullptr,
697         nullptr
698     };
699     // clang-format on
700
701     InitTestInetCommon();
702
703     InitSystemLayer();
704     InitNetwork();
705
706     // Run all tests in Suite
707
708     nlTestRunner(&DNSTestSuite, nullptr);
709
710     ShutdownNetwork();
711     ShutdownSystemLayer();
712
713     return nlTestRunnerStats(&DNSTestSuite);
714 }
715
716 CHIP_REGISTER_TEST_SUITE(TestInetLayerDNSInternal)
717 #else // !INET_CONFIG_ENABLE_DNS_RESOLVER
718
719 int TestInetLayerDNSInternal(void)
720 {
721     fprintf(stderr, "Please assert INET_CONFIG_ENABLE_DNS_RESOLVER to use this test.\n");
722
723     return (EXIT_SUCCESS);
724 }
725
726 #endif // !INET_CONFIG_ENABLE_DNS_RESOLVER
727
728 int TestInetLayerDNS()
729 {
730     SetSignalHandler(HandleSIGUSR1);
731
732     nlTestSetOutputStyle(OUTPUT_CSV);
733
734     return (TestInetLayerDNSInternal());
735 }