1#
2#   Copyright 2018 - The Android Open Source Project
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
16import logging
17import os
18import random
19import socket
20import threading
21import time
22
23from acts import asserts
24from acts import base_test
25from acts import test_runner
26from acts.controllers import adb
27from acts.test_decorators import test_tracker_info
28from acts.test_utils.net import connectivity_const as cconst
29from acts.test_utils.net import connectivity_test_utils as cutils
30from acts.test_utils.net import net_test_utils as nutils
31from acts.test_utils.net.net_test_utils import start_tcpdump
32from acts.test_utils.net.net_test_utils import stop_tcpdump
33from acts.test_utils.tel import tel_test_utils as tutils
34from acts.test_utils.tel.tel_defines import WFC_MODE_DISABLED
35from acts.test_utils.tel.tel_test_utils import get_operator_name
36from acts.test_utils.tel.tel_test_utils import set_wfc_mode
37from acts.test_utils.wifi import wifi_test_utils as wutils
38
39from scapy.all import TCP
40from scapy.all import UDP
41from scapy.all import rdpcap
42from scapy.all import Scapy_Exception
43
44RST = 0x04
45SSID = wutils.WifiEnums.SSID_KEY
46
47class DnsOverTlsTest(base_test.BaseTestClass):
48    """ Tests for Wifi Tethering """
49
50    def setup_class(self):
51        """ Setup devices for tethering and unpack params """
52
53        self.dut = self.android_devices[0]
54        self.dut_b = self.android_devices[1]
55        for ad in self.android_devices:
56            nutils.verify_lte_data_and_tethering_supported(ad)
57            set_wfc_mode(self.log, ad, WFC_MODE_DISABLED)
58        req_params = ("ping_hosts", "ipv4_only_network", "ipv4_ipv6_network",)
59        self.unpack_userparams(req_params)
60        self.tcpdump_pid = None
61        self.private_dns_servers = [cconst.DNS_GOOGLE,
62                                    cconst.DNS_QUAD9,
63                                    cconst.DNS_CLOUDFLARE]
64
65    def teardown_test(self):
66        wutils.reset_wifi(self.dut)
67
68    def on_fail(self, test_name, begin_time):
69        self.dut.take_bug_report(test_name, begin_time)
70
71    """ Helper functions """
72
73    def _start_tcp_dump(self, ad):
74        """ Start tcpdump on the give dut
75
76        Args:
77            1. ad: dut to run tcpdump on
78        """
79        self.tcpdump_pid = start_tcpdump(ad, self.test_name)
80
81    def _stop_tcp_dump(self, ad):
82        """ Stop tcpdump and pull it to the test run logs
83
84        Args:
85            1. ad: dut to pull tcpdump from
86        """
87        return stop_tcpdump(ad, self.tcpdump_pid, self.test_name)
88
89    def _verify_dns_queries_over_tls(self, pcap_file, tls=True):
90        """ Verify if DNS queries were over TLS or not
91
92        Args:
93            1. pcap_file: tcpdump file
94            2. tls: if queries should be over TLS or port 853
95        """
96        try:
97            packets = rdpcap(pcap_file)
98        except Scapy_Exception:
99            asserts.fail("Not a valid pcap file")
100        for pkt in packets:
101            summary = "%s" % pkt.summary()
102            for host in self.ping_hosts:
103                host = host.split('.')[-2]
104                if tls and UDP in pkt and pkt[UDP].dport == 53 and \
105                    host in summary:
106                      asserts.fail("Found query to port 53: %s" % summary)
107                elif not tls and TCP in pkt and pkt[TCP].dport == 853 and \
108                    not pkt[TCP].flags:
109                      asserts.fail("Found query to port 853: %s" % summary)
110
111    def _verify_rst_packets(self, pcap_file):
112        """ Verify if RST packets are found in the pcap file
113
114        Args:
115            1. pcap_file: full path of tcpdump file
116        """
117        packets = rdpcap(pcap_file)
118        for pkt in packets:
119            if TCP in pkt and pkt[TCP].flags == RST and pkt[TCP].dport == 853:
120                asserts.fail("Found RST packets: %s" % pkt.summary())
121
122    def _test_private_dns_mode(self, ad, net, dns_mode, use_tls, hostname=None):
123        """ Test private DNS mode
124
125        Args:
126            1. ad: android device object
127            2. net: wifi network to connect to, LTE network if None
128            3. dns_mode: private DNS mode
129            4. use_tls: if True, the DNS packets should be encrypted
130            5. hostname: private DNS hostname to set to
131        """
132
133        # set private dns mode
134        if dns_mode:
135            cutils.set_private_dns(self.dut, dns_mode, hostname)
136
137        # connect to wifi
138        if net:
139            wutils.start_wifi_connection_scan_and_ensure_network_found(
140                self.dut, net[SSID])
141            wutils.wifi_connect(self.dut, net)
142
143        # start tcpdump on the device
144        self._start_tcp_dump(self.dut)
145
146        # ping hosts should pass
147        for host in self.ping_hosts:
148            self.log.info("Pinging %s" % host)
149            status = wutils.validate_connection(self.dut, host)
150            asserts.assert_true(status, "Failed to ping host %s" % host)
151            self.log.info("Ping successful")
152
153        # stop tcpdump
154        pcap_file = self._stop_tcp_dump(self.dut)
155
156        # verify DNS queries
157        self._verify_dns_queries_over_tls(pcap_file, use_tls)
158
159        # reset wifi
160        wutils.reset_wifi(self.dut)
161
162    """ Test Cases """
163
164    @test_tracker_info(uuid="2957e61c-d333-45fb-9ff9-2250c9c8535a")
165    def test_private_dns_mode_off_wifi_ipv4_only_network(self):
166        """ Verify private dns mode off on ipv4 only network
167
168        Steps:
169            1. Set private dns mode off
170            2. Connect to wifi network. DNS server supports DNS/TLS
171            3. Run HTTP ping to amazon.com, facebook.com, netflix.com
172            4. Verify ping works to differnt hostnames
173            5. Verify that all queries go to port 53
174        """
175        self._test_private_dns_mode(self.dut,
176                                    self.ipv4_only_network,
177                                    cconst.PRIVATE_DNS_MODE_OFF,
178                                    False)
179
180    @test_tracker_info(uuid="ea036d22-25af-4df0-b6cc-0027bc1efbe9")
181    def test_private_dns_mode_off_wifi_ipv4_ipv6_network(self):
182        """ Verify private dns mode off on ipv4-ipv6 network
183
184        Steps:
185            1. Set private dns mode off
186            2. Connect to wifi network. DNS server supports DNS/TLS
187            3. Run HTTP ping to amazon.com, facebook.com, netflix.com
188            4. Verify ping works to differnt hostnames
189            5. Verify that all queries go to port 53
190        """
191        self._test_private_dns_mode(self.dut,
192                                    self.ipv4_ipv6_network,
193                                    cconst.PRIVATE_DNS_MODE_OFF,
194                                    False)
195
196    @test_tracker_info(uuid="4227abf4-0a75-4b4d-968c-dfc63052f5db")
197    def test_private_dns_mode_opportunistic_wifi_ipv4_only_network(self):
198        """ Verify private dns mode opportunistic on ipv4 only network
199
200        Steps:
201            1. Set private dns to opportunistic mode
202            2. Connect to wifi network. DNS server supports DNS/TLS
203            3. Run HTTP ping to amazon.com, facebook.com, netflix.com
204            4. Verify ping works to differnt hostnames
205            5. Verify that all queries go to port 853 and encrypted
206        """
207        self._test_private_dns_mode(self.dut,
208                                    self.ipv4_only_network,
209                                    cconst.PRIVATE_DNS_MODE_OPPORTUNISTIC,
210                                    True)
211
212    @test_tracker_info(uuid="0c97cfef-4313-4346-b05b-395de63c5c3f")
213    def test_private_dns_mode_opportunistic_wifi_ipv4_ipv6_network(self):
214        """ Verify private dns mode opportunistic on ipv4-ipv6 network
215
216        Steps:
217            1. Set private dns to opportunistic mode
218            2. Connect to wifi network. DNS server supports DNS/TLS
219            3. Run HTTP ping to amazon.com, facebook.com, netflix.com
220            4. Verify ping works to differnt hostnames
221            5. Verify that all queries go to port 853
222        """
223        self._test_private_dns_mode(self.dut,
224                                    self.ipv4_ipv6_network,
225                                    cconst.PRIVATE_DNS_MODE_OPPORTUNISTIC,
226                                    True)
227
228    @test_tracker_info(uuid="b70569f1-2613-49d0-be49-fd3464dde305")
229    def test_private_dns_mode_strict_wifi_ipv4_only_network(self):
230        """ Verify private dns mode strict on ipv4 only network
231
232        Steps:
233            1. Set private dns to strict mode
234            2. Connect to wifi network. DNS server supports DNS/TLS
235            3. Run HTTP ping to amazon.com, facebook.com, netflix.com
236            4. Verify ping works to differnt hostnames
237            5. Verify that all queries go to port 853 and encrypted
238        """
239        for dns in self.private_dns_servers:
240            self._test_private_dns_mode(self.dut,
241                                        self.ipv4_only_network,
242                                        cconst.PRIVATE_DNS_MODE_STRICT,
243                                        True,
244                                        dns)
245
246    @test_tracker_info(uuid="85738b52-823b-4c59-a0d5-219e2fab2929")
247    def test_private_dns_mode_strict_wifi_ipv4_ipv6_network(self):
248        """ Verify private dns mode strict on ipv4-ipv6 network
249
250        Steps:
251            1. Set private dns to strict mode
252            2. Connect to wifi network. DNS server supports DNS/TLS
253            3. Run HTTP ping to amazon.com, facebook.com, netflix.com
254            4. Verify ping works to differnt hostnames
255            5. Verify that all queries go to port 853 and encrypted
256        """
257        for dns in self.private_dns_servers:
258            self._test_private_dns_mode(self.dut,
259                                        self.ipv4_ipv6_network,
260                                        cconst.PRIVATE_DNS_MODE_STRICT,
261                                        True,
262                                        dns)
263
264    @test_tracker_info(uuid="727e280a-d2bd-463f-b2a1-653d4b3f7f29")
265    def test_private_dns_mode_off_vzw_carrier(self):
266        """ Verify private dns mode off on VZW network
267
268        Steps:
269            1. Set private dns mode off
270            2. Connect to wifi network. VZW doesn't support DNS/TLS
271            3. Run HTTP ping to amazon.com, facebook.com, netflix.com
272            4. Verify ping works to differnt hostnames
273            5. Verify that all queries go to port 53
274        """
275        carrier = get_operator_name(self.log, self.dut_b)
276        asserts.skip_if(carrier != "vzw", "Carrier is not Verizon")
277        self._test_private_dns_mode(self.dut_b,
278                                    None,
279                                    cconst.PRIVATE_DNS_MODE_OFF,
280                                    False)
281
282    @test_tracker_info(uuid="b16f6e2c-a24f-4efe-9003-2bfaf28b8d5e")
283    def test_private_dns_mode_off_tmo_carrier(self):
284        """ Verify private dns mode off on TMO network
285
286        Steps:
287            1. Set private dns to off mode
288            2. Connect to wifi network. TMO supports DNS/TLS
289            3. Run HTTP ping to amazon.com, facebook.com, netflix.com
290            4. Verify ping works to differnt hostnames
291            5. Verify that all queries go to port 53
292        """
293        carrier = get_operator_name(self.log, self.dut)
294        asserts.skip_if(carrier != "tmo", "Carrier is not T-mobile")
295        self._test_private_dns_mode(self.dut,
296                                    None,
297                                    cconst.PRIVATE_DNS_MODE_OFF,
298                                    False)
299
300    @test_tracker_info(uuid="edfa7bfe-3e52-46b4-9d72-7c6c300b3680")
301    def test_private_dns_mode_opportunistic_vzw_carrier(self):
302        """ Verify private dns mode opportunistic on VZW network
303
304        Steps:
305            1. Set private dns mode opportunistic
306            2. Connect to wifi network. VZW doesn't support DNS/TLS
307            3. Run HTTP ping to amazon.com, facebook.com, netflix.com
308            4. Verify ping works to differnt hostnames
309            5. Verify that all queries go to port 53
310        """
311        carrier = get_operator_name(self.log, self.dut_b)
312        asserts.skip_if(carrier != "vzw", "Carrier is not Verizon")
313        self._test_private_dns_mode(self.dut_b,
314                                    None,
315                                    cconst.PRIVATE_DNS_MODE_OPPORTUNISTIC,
316                                    False)
317
318    @test_tracker_info(uuid="41c3f2c4-11b7-4bb8-a3c9-fac63f6822f6")
319    def test_private_dns_mode_opportunistic_tmo_carrier(self):
320        """ Verify private dns mode opportunistic on TMO network
321
322        Steps:
323            1. Set private dns mode opportunistic
324            2. Connect to wifi network. TMP supports DNS/TLS
325            3. Run HTTP ping to amazon.com, facebook.com, netflix.com
326            4. Verify ping works to differnt hostnames
327            5. Verify that all queries go to port 853 and encrypted
328        """
329        carrier = get_operator_name(self.log, self.dut)
330        asserts.skip_if(carrier != "tmo", "Carrier is not T-mobile")
331        self._test_private_dns_mode(self.dut,
332                                    None,
333                                    cconst.PRIVATE_DNS_MODE_OPPORTUNISTIC,
334                                    True)
335
336    @test_tracker_info(uuid="65fd2052-f0c0-4446-b353-7ed2273e6c95")
337    def test_private_dns_mode_strict_vzw_carrier(self):
338        """ Verify private dns mode strict on VZW network
339
340        Steps:
341            1. Set private dns mode strict
342            2. Connect to wifi network. VZW doesn't support DNS/TLS
343            3. Run HTTP ping to amazon.com, facebook.com, netflix.com
344            4. Verify ping works to differnt hostnames
345            5. Verify that all queries go to port 853 and encrypted
346        """
347        carrier = get_operator_name(self.log, self.dut_b)
348        asserts.skip_if(carrier != "vzw", "Carrier is not Verizon")
349        for dns in self.private_dns_servers:
350            self._test_private_dns_mode(self.dut_b,
351                                        None,
352                                        cconst.PRIVATE_DNS_MODE_STRICT,
353                                        True,
354                                        dns)
355
356    @test_tracker_info(uuid="bca141f7-06c9-4e44-854e-4bdb9443b2da")
357    def test_private_dns_mode_strict_tmo_carrier(self):
358        """ Verify private dns mode strict on TMO network
359
360        Steps:
361            1. Set private dns mode strict
362            2. Connect to wifi network. TMO supports DNS/TLS
363            3. Run HTTP ping to amazon.com, facebook.com, netflix.com
364            4. Verify ping works to differnt hostnames
365            5. Verify that all queries go to port 853 and encrypted
366        """
367        carrier = get_operator_name(self.log, self.dut)
368        asserts.skip_if(carrier != "tmo", "Carrier is not T-mobile")
369        for dns in self.private_dns_servers:
370            self._test_private_dns_mode(self.dut,
371                                        None,
372                                        cconst.PRIVATE_DNS_MODE_STRICT,
373                                        True,
374                                        dns)
375
376    @test_tracker_info(uuid="7d977987-d9e3-4be1-b8fc-e5a84050ed48")
377    def test_private_dns_mode_opportunistic_connectivity_toggle_networks(self):
378        """ Verify private DNS opportunistic mode connectivity by toggling networks
379
380        Steps:
381            1. Set private DNS opportunistic mode
382            2. DUT is connected to mobile network
383            3. Verify connectivity and DNS queries going to port 853 for TMO
384               and port 53 for VZW
385            4. Switch to wifi network set with private DNS server
386            5. Verify connectivity and DNS queries going to port 853
387            6. Switch back to mobile network
388            7. Verify connectivity and DNS queries going to port 853 for TMO
389               and port 53 for VZW
390            8. Repeat steps 1-7 for TMO, VZW and different private DNS servers
391        """
392        for ad in self.android_devices:
393            carrier = get_operator_name(self.log, ad)
394            self.log.info("Carrier is: %s" % carrier)
395            use_tls = True if carrier == "tmo" else False
396            for dns in self.private_dns_servers:
397                self.log.info("Setting opportunistic private dns mode")
398                # set private dns mode
399                cutils.set_private_dns(ad, cconst.PRIVATE_DNS_MODE_OPPORTUNISTIC)
400
401                # verify dns over tls on mobile network
402                self._test_private_dns_mode(
403                    self.dut, None, None, use_tls, dns)
404
405                # verify dns over tls on wifi network
406                self._test_private_dns_mode(
407                    self.dut, self.ipv4_ipv6_network, None, True, dns)
408
409                # verify dns over tls on mobile network
410                wutils.reset_wifi(self.dut)
411                self._test_private_dns_mode(
412                    self.dut, None, None, use_tls, dns)
413
414    @test_tracker_info(uuid="bc2f228f-e288-4539-a4b9-c02968209985")
415    def test_private_dns_mode_strict_connectivity_toggle_networks(self):
416        """ Verify private DNS strict mode connectivity by toggling networks
417
418        Steps:
419            1. Set private DNS strict mode
420            2. DUT is connected to mobile network
421            3. Verify connectivity and DNS queries going to port 853
422            4. Switch to wifi network
423            5. Verify connectivity and DNS queries going to port 853
424            6. Switch back to mobile network
425            7. Verify connectivity and DNS queries going to port 853
426            8. Repeat steps 1-7 for TMO, VZW and different private DNS servers
427        """
428        for ad in self.android_devices:
429            self.log.info("Carrier is: %s" % get_operator_name(self.log, ad))
430            for dns in self.private_dns_servers:
431                self.log.info("Setting strict mode private dns: %s" % dns)
432                # set private dns mode
433                cutils.set_private_dns(ad, cconst.PRIVATE_DNS_MODE_STRICT, dns)
434
435                # verify dns over tls on mobile network
436                self._test_private_dns_mode(
437                    self.dut, None, None, True, dns)
438
439
440                # verify dns over tls on wifi network
441                self._test_private_dns_mode(
442                    self.dut, self.ipv4_ipv6_network, None, True, dns)
443
444                # verify dns over tls on mobile network
445                wutils.reset_wifi(self.dut)
446                self._test_private_dns_mode(
447                    self.dut, None, None, True, dns)
448
449    @test_tracker_info(uuid="1426673a-7728-4df7-8de5-dfb3529ada62")
450    def test_dns_server_link_properties_strict_mode(self):
451        """ Verify DNS server in the link properties when set in strict mode
452
453        Steps:
454            1. Set DNS server hostname in Private DNS settings (stict mode)
455            2. Verify that DNS server set in settings is in link properties
456            3. Verify for WiFi as well as LTE
457        """
458        # start tcpdump on device
459        self._start_tcp_dump(self.dut)
460
461        # set private DNS to strict mode
462        cutils.set_private_dns(
463            self.dut, cconst.PRIVATE_DNS_MODE_STRICT, cconst.DNS_GOOGLE)
464
465        # connect DUT to wifi network
466        wutils.start_wifi_connection_scan_and_ensure_network_found(
467            self.dut, self.ipv4_ipv6_network[SSID])
468        wutils.wifi_connect(self.dut, self.ipv4_ipv6_network)
469        for host in self.ping_hosts:
470            wutils.validate_connection(self.dut, host)
471
472        # DNS server in link properties for wifi network
473        link_prop = self.dut.droid.connectivityGetActiveLinkProperties()
474        wifi_dns_servers = link_prop['PrivateDnsServerName']
475        self.log.info("Link prop: %s" % wifi_dns_servers)
476
477        # DUT is on LTE data
478        wutils.reset_wifi(self.dut)
479        time.sleep(1) # wait till lte network becomes active
480        for host in self.ping_hosts:
481            wutils.validate_connection(self.dut, host)
482
483        # DNS server in link properties for cell network
484        link_prop = self.dut.droid.connectivityGetActiveLinkProperties()
485        lte_dns_servers = link_prop['PrivateDnsServerName']
486        self.log.info("Link prop: %s" % lte_dns_servers)
487
488        # stop tcpdump on device
489        pcap_file = self._stop_tcp_dump(self.dut)
490
491        # Verify DNS server in link properties
492        asserts.assert_true(cconst.DNS_GOOGLE in wifi_dns_servers,
493                            "Hostname not in link properties - wifi network")
494        asserts.assert_true(cconst.DNS_GOOGLE in lte_dns_servers,
495                            "Hostname not in link properites - cell network")
496
497    @test_tracker_info(uuid="525a6f2d-9751-474e-a004-52441091e427")
498    def test_dns_over_tls_no_reset_packets(self):
499        """ Verify there are no TCP packets with RST flags
500
501        Steps:
502            1. Enable opportunistic or strict mode
503            2. Ping hosts and verify that there are TCP pkts with RST flags
504        """
505        # start tcpdump on device
506        self._start_tcp_dump(self.dut)
507
508        # set private DNS to opportunistic mode
509        cutils.set_private_dns(self.dut, cconst.PRIVATE_DNS_MODE_OPPORTUNISTIC)
510
511        # connect DUT to wifi network
512        wutils.start_wifi_connection_scan_and_ensure_network_found(
513            self.dut, self.ipv4_ipv6_network[SSID])
514        wutils.wifi_connect(self.dut, self.ipv4_ipv6_network)
515        for host in self.ping_hosts:
516            wutils.validate_connection(self.dut, host)
517
518        # stop tcpdump on device
519        pcap_file = self._stop_tcp_dump(self.dut)
520
521        # check that there no RST TCP packets
522        self._verify_rst_packets(pcap_file)
523
524    @test_tracker_info(uuid="af6e34f1-3ad5-4ab0-b3b9-53008aa08294")
525    def test_private_dns_mode_strict_invalid_hostnames(self):
526        """ Verify that invalid hostnames are not saved for strict mode
527
528        Steps:
529            1. Set private DNS to strict mode with invalid hostname
530            2. Verify that invalid hostname is not saved
531        """
532        invalid_hostnames = ["!%@&!*", "12093478129", "9.9.9.9", "sdkfjhasdf"]
533        for hostname in invalid_hostnames:
534            cutils.set_private_dns(
535                self.dut, cconst.PRIVATE_DNS_MODE_STRICT, hostname)
536            mode = self.dut.droid.getPrivateDnsMode()
537            specifier = self.dut.droid.getPrivateDnsSpecifier()
538            asserts.assert_true(
539                mode == cconst.PRIVATE_DNS_MODE_STRICT and specifier != hostname,
540                "Able to set invalid private DNS strict mode")
541