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.tel.tel_data_utils import wait_for_cell_data_connection
29from acts.test_utils.tel.tel_test_utils import start_adb_tcpdump
30from acts.test_utils.tel.tel_test_utils import stop_adb_tcpdump
31from acts.test_utils.tel.tel_test_utils import verify_http_connection
32from acts.test_utils.wifi import wifi_test_utils as wutils
33
34from scapy.all import TCP
35from scapy.all import UDP
36from scapy.all import rdpcap
37
38DNS_QUAD9 = "dns.quad9.net"
39PRIVATE_DNS_MODE_OFF = "off"
40PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic"
41PRIVATE_DNS_MODE_STRICT = "hostname"
42RST = 0x04
43
44class DnsOverTlsTest(base_test.BaseTestClass):
45    """ Tests for Wifi Tethering """
46
47    def setup_class(self):
48        """ Setup devices for tethering and unpack params """
49
50        self.dut = self.android_devices[0]
51        wutils.reset_wifi(self.dut)
52        self.dut.droid.telephonyToggleDataConnection(True)
53        wait_for_cell_data_connection(self.log, self.dut, True)
54        asserts.assert_true(
55            verify_http_connection(self.log, self.dut),
56            "HTTP verification failed on cell data connection")
57        req_params = ("wifi_network_with_dns_tls", "wifi_network_no_dns_tls",
58                      "ping_hosts")
59        self.unpack_userparams(req_params)
60        self.tcpdump_pid = None
61        self.tcpdump_file = None
62
63    """ Helper functions """
64
65    def _start_tcp_dump(self, ad):
66        """ Start tcpdump on the give dut
67
68        Args:
69            1. ad: dut to run tcpdump on
70        """
71        if self.tcpdump_pid:
72            stop_adb_tcpdump(ad, self.tcpdump_pid, pull_tcpdump=False)
73        self.tcpdump_pid = start_adb_tcpdump(ad, self.test_name, mask='all')
74
75    def _stop_tcp_dump(self, ad):
76        """ Stop tcpdump and pull it to the test run logs
77
78        Args:
79            1. ad: dut to pull tcpdump from
80        """
81        file_name = ad.adb.shell("ls /sdcard/tcpdump")
82        file_name = os.path.join(ad.log_path, "TCPDUMP_%s" % ad.serial,
83                                 file_name.split('/')[-1])
84        if self.tcpdump_pid:
85            stop_adb_tcpdump(ad, self.tcpdump_pid, pull_tcpdump=True)
86            self.tcpdump_pid = None
87        return os.path.join(ad.log_path, file_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            if tls and UDP in pkt and pkt[UDP].dport == 53 and \
103                "connectivitycheck.gstatic.com." not in summary and \
104                "www.google.com" not in summary:
105                asserts.fail("Found query to port 53: %s" % summary)
106            elif not tls and TCP in pkt and pkt[TCP].dport == 853 and \
107                not pkt[TCP].flags:
108                asserts.fail("Found query to port 853: %s" % summary)
109
110    def _verify_rst_packets(self, pcap_file):
111        """ Verify if RST packets are found in the pcap file """
112        packets = rdpcap(pcap_file)
113        for pkt in packets:
114            if TCP in pkt and pkt[TCP].flags == RST:
115                asserts.fail("Found RST packets: %s" % pkt.summary())
116
117    def _test_private_dns_mode(self, network, dns_mode, use_tls,
118                               hostname = None):
119        """ Test private DNS mode """
120        # connect to wifi
121        wutils.reset_wifi(self.dut)
122        if network:
123            wutils.wifi_connect(self.dut, network)
124        time.sleep(1) # wait till lte network becomes active - network = None
125
126        # start tcpdump on the device
127        self._start_tcp_dump(self.dut)
128
129        # set private dns mode
130        if dns_mode == PRIVATE_DNS_MODE_OFF:
131            self.dut.droid.setPrivateDnsMode(False)
132        elif hostname:
133            self.dut.droid.setPrivateDnsMode(True, hostname)
134        else:
135            self.dut.droid.setPrivateDnsMode(True)
136        mode = self.dut.droid.getPrivateDnsMode()
137        asserts.assert_true(mode == dns_mode,
138                            "Failed to set private DNS mode to %s" % dns_mode)
139
140        # ping hosts should pass
141        for host in self.ping_hosts:
142            self.log.info("Pinging %s" % host)
143            asserts.assert_true(wutils.validate_connection(self.dut, host),
144                                "Failed to ping host %s" % host)
145
146        # stop tcpdump
147        pcap_file = self._stop_tcp_dump(self.dut)
148        self.log.info("TCPDUMP file is: %s" % pcap_file)
149
150        # verify DNS queries
151        self._verify_dns_queries_over_tls(pcap_file, use_tls)
152
153    """ Test Cases """
154
155    @test_tracker_info(uuid="2957e61c-d333-45fb-9ff9-2250c9c8535a")
156    def test_private_dns_mode_off_wifi_no_dns_tls_server(self):
157        """ Verify private dns mode off
158
159        Steps:
160            1. Set private dns mode off
161            2. Connect to wifi network. DNS/TLS server is not set
162            3. Verify ping works to differnt hostnames
163            4. Verify that all queries go to port 53
164        """
165        self._test_private_dns_mode(self.wifi_network_no_dns_tls,
166                                    PRIVATE_DNS_MODE_OFF, False)
167
168    @test_tracker_info(uuid="ea036d22-25af-4df0-b6cc-0027bc1efbe9")
169    def test_private_dns_mode_off_wifi_with_dns_tls_server(self):
170        """ Verify private dns mode off
171
172        Steps:
173            1. Set private dns mode off
174            2. Connect to wifi network. DNS server is set to 9.9.9.9, 8.8.8.8
175            3. Verify ping works to differnt hostnames
176            4. Verify that all queries go to port 53
177        """
178        self._test_private_dns_mode(self.wifi_network_with_dns_tls,
179                                    PRIVATE_DNS_MODE_OFF, False)
180
181    @test_tracker_info(uuid="4227abf4-0a75-4b4d-968c-dfc63052f5db")
182    def test_private_dns_mode_opportunistic_wifi_no_dns_tls_server(self):
183        """ Verify private dns opportunistic mode
184
185        Steps:
186            1. Set private dns mode to opportunistic
187            2. Connect to wifi network. DNS/TLS server is not set
188            3. Verify ping works to differnt hostnames
189            4. Verify that all queries go to port 53
190        """
191        self._test_private_dns_mode(self.wifi_network_no_dns_tls,
192                                    PRIVATE_DNS_MODE_OPPORTUNISTIC, False)
193
194    @test_tracker_info(uuid="0c97cfef-4313-4346-b05b-395de63c5c3f")
195    def test_private_dns_mode_opportunistic_wifi_with_dns_tls_server(self):
196        """ Verify private dns opportunistic mode
197
198        Steps:
199            1. Set private dns mode to opportunistic
200            2. Connect to wifi network. DNS server is set to 9.9.9.9, 8.8.8.8
201            3. Verify ping works to differnt hostnames
202            4. Verify that all queries go to port 853
203        """
204        self._test_private_dns_mode(self.wifi_network_with_dns_tls,
205                                    PRIVATE_DNS_MODE_OPPORTUNISTIC, True)
206
207    @test_tracker_info(uuid="b70569f1-2613-49d0-be49-fd3464dde305")
208    def test_private_dns_mode_strict_wifi_no_dns_tls_server(self):
209        """ Verify private dns strict mode
210
211        Steps:
212            1. Set private dns mode to strict
213            2. Connect to wifi network. DNS/TLS server is not set
214            3. Verify ping works to differnt hostnames
215            4. Verify that all queries go to port 853
216        """
217        self._test_private_dns_mode(self.wifi_network_no_dns_tls,
218                                    PRIVATE_DNS_MODE_STRICT, True,
219                                    DNS_QUAD9)
220
221    @test_tracker_info(uuid="85738b52-823b-4c59-a0d5-219e2fab2929")
222    def test_private_dns_mode_strict_wifi_with_dns_tls_server(self):
223        """ Verify private dns strict mode
224
225        Steps:
226            1. Set private dns mode to strict
227            2. Connect to wifi network. DNS server is set to 9.9.9.9, 8.8.8.8
228            3. Verify ping works to differnt hostnames
229            4. Verify that all queries go to port 853
230        """
231        self._test_private_dns_mode(self.wifi_network_with_dns_tls,
232                                    PRIVATE_DNS_MODE_STRICT, True,
233                                    DNS_QUAD9)
234
235    @test_tracker_info(uuid="727e280a-d2bd-463f-b2a1-653d4b3f7f29")
236    def test_private_dns_mode_off_lte(self):
237        """ Verify private dns off mode
238
239        Steps:
240            1. Set private dns mode to off
241            2. Reset wifi and enable LTE on DUT
242            3. Verify ping works to differnt hostnames
243            4. Verify that all queries go to port 53
244        """
245        self._test_private_dns_mode(None, PRIVATE_DNS_MODE_OFF, False)
246
247    @test_tracker_info(uuid="b16f6e2c-a24f-4efe-9003-2bfaf28b8d5e")
248    def test_private_dns_mode_opportunistic_lte(self):
249        """ Verify private dns opportunistic mode
250
251        Steps:
252            1. Set private dns mode to opportunistic mode
253            2. Reset wifi and enable LTE on DUT
254            3. Verify ping works to differnt hostnames
255            4. Verify that all queries go to port 853
256        """
257        self._test_private_dns_mode(None, PRIVATE_DNS_MODE_OPPORTUNISTIC, True)
258
259    @test_tracker_info(uuid="edfa7bfe-3e52-46b4-9d72-7c6c300b3680")
260    def test_private_dns_mode_strict_lte(self):
261        """ Verify private dns strict mode
262
263        Steps:
264            1. Set private dns mode to strict mode
265            2. Reset wifi and enable LTE on DUT
266            3. Verify ping works to differnt hostnames
267            4. Verify that all queries go to port 853
268        """
269        self._test_private_dns_mode(None, PRIVATE_DNS_MODE_STRICT, True,
270                                    DNS_QUAD9)
271
272    @test_tracker_info(uuid="1426673a-7728-4df7-8de5-dfb3529ada62")
273    def test_dns_server_link_properties_strict_mode(self):
274        """ Verify DNS server in the link properties when set in strict mode
275
276        Steps:
277            1. Set DNS server hostname in Private DNS settings (stict mode)
278            2. Verify that DNS server set in settings is in link properties
279            3. Verify for WiFi as well as LTE
280        """
281        # start tcpdump on device
282        self._start_tcp_dump(self.dut)
283
284        # set private DNS to strict mode
285        self.dut.droid.setPrivateDnsMode(True, DNS_QUAD9)
286        mode = self.dut.droid.getPrivateDnsMode()
287        specifier = self.dut.droid.getPrivateDnsSpecifier()
288        asserts.assert_true(
289            mode == PRIVATE_DNS_MODE_STRICT and specifier == DNS_QUAD9,
290            "Failed to set private DNS strict mode")
291
292        # connect DUT to wifi network
293        wutils.wifi_connect(self.dut, self.wifi_network_no_dns_tls)
294        for host in self.ping_hosts:
295            wutils.validate_connection(self.dut, host)
296
297        # DNS server in link properties for wifi network
298        link_prop = self.dut.droid.connectivityGetActiveLinkProperties()
299        dns_servers = link_prop['DnsServers']
300        wifi_dns_servers = [each for lst in dns_servers for each in lst]
301        self.log.info("Link prop: %s" % wifi_dns_servers)
302
303        # DUT is on LTE data
304        wutils.reset_wifi(self.dut)
305        time.sleep(1) # wait till lte network becomes active
306        for host in self.ping_hosts:
307            wutils.validate_connection(self.dut, host)
308
309        # DNS server in link properties for cell network
310        link_prop = self.dut.droid.connectivityGetActiveLinkProperties()
311        dns_servers = link_prop['DnsServers']
312        lte_dns_servers = [each for lst in dns_servers for each in lst]
313        self.log.info("Link prop: %s" % lte_dns_servers)
314
315        # stop tcpdump on device
316        pcap_file = self._stop_tcp_dump(self.dut)
317        self.log.info("TCPDUMP file is: %s" % pcap_file)
318
319        # Verify DNS server in link properties
320        asserts.assert_true(DNS_QUAD9 in wifi_dns_servers,
321                            "Hostname not in link properties - wifi network")
322        asserts.assert_true(DNS_QUAD9 in lte_dns_servers,
323                            "Hostname not in link properites - cell network")
324
325    @test_tracker_info(uuid="525a6f2d-9751-474e-a004-52441091e427")
326    def test_dns_over_tls_no_reset_packets(self):
327        """ Verify there are no TCP packets with RST flags
328
329        Steps:
330            1. Enable opportunistic or strict mode
331            2. Ping hosts and verify that there are TCP pkts with RST flags
332        """
333        # start tcpdump on device
334        self._start_tcp_dump(self.dut)
335
336        # set private DNS to opportunistic mode
337        self.dut.droid.setPrivateDnsMode(True)
338        mode = self.dut.droid.getPrivateDnsMode()
339        asserts.assert_true(mode == PRIVATE_DNS_MODE_OPPORTUNISTIC,
340                            "Failed to set private DNS opportunistic mode")
341
342        # connect DUT to wifi network
343        wutils.wifi_connect(self.dut, self.wifi_network_with_dns_tls)
344        for host in self.ping_hosts:
345            wutils.validate_connection(self.dut, host)
346
347        # stop tcpdump on device
348        pcap_file = self._stop_tcp_dump(self.dut)
349        self.log.info("TCPDUMP file is: %s" % pcap_file)
350
351        # check that there no RST TCP packets
352        self._verify_rst_packets(pcap_file)
353
354    @test_tracker_info(uuid="af6e34f1-3ad5-4ab0-b3b9-53008aa08294")
355    def test_private_dns_mode_strict_invalid_hostnames(self):
356        """ Verify that invalid hostnames are not saved for strict mode
357
358        Steps:
359            1. Set private DNS to strict mode with invalid hostname
360            2. Verify that invalid hostname is not saved
361        """
362        invalid_hostnames = ["!%@&!*", "12093478129", "9.9.9.9", "sdkfjhasdf"]
363        for hostname in invalid_hostnames:
364            self.dut.droid.setPrivateDnsMode(True, hostname)
365            mode = self.dut.droid.getPrivateDnsMode()
366            specifier = self.dut.droid.getPrivateDnsSpecifier()
367            wutils.wifi_connect(self.dut, self.wifi_network_no_dns_tls)
368            asserts.assert_true(
369                mode == PRIVATE_DNS_MODE_STRICT and specifier != hostname,
370                "Able to set invalid private DNS strict mode")
371