def test(self): L.describe('Find all known DNS servers before connecting to VPN') dns_servers_before_connect = self.localhost['dns_tool'].known_servers() L.info( "All known DNS servers are: {}".format(dns_servers_before_connect)) L.describe('Open and connect the VPN application') self.localhost['vpn_application'].open_and_connect() vpn_dns_servers = self.localhost['vpn_application'].dns_server_ips() L.info("VPN DNS servers are: {}".format(vpn_dns_servers)) L.describe('Check DNS server used was a VPN DNS server') DNSHelper(self.localhost['dns_tool']).dns_server_is_vpn_server( dns_servers_before_connect, vpn_dns_servers)
class TestDNSVanillaAggressive(LocalTestCase): '''Summary: Test whether DNS leaks during regular VPN connection. Details: This test will first ask the system for all known DNS servers. It will then connect to the VPN and perform multiple DNS requests in parallel. It asserts that the server used for the DNS lookup was not one which was known to the system before connecting to the VPN and that it was also one of the VPN server IPs. There is redundancy in this check but no harm. Discussion: The test is very similar to TestDNSVanilla but just performs multiple DNS lookups. Most providers should pass this test. The Weaknesses: Currently uses dig to decide if DNS leaks. This isn't reliable for some VPN providers. Some providers intercept DNS upstream and change the destination DNS server to their own server. However dig will still report the server which it originally sent the request to. Scenarios: * Run on a system with DNS servers configured to be public IP addresses, e.g. 8.8.8.8. * Run on a system with DNS servers configured to be local IP addresses, e.g. 192.0.0.0/24. This is a common setup with home routers where the router acts as the DNS server. ''' # TODO: Potentially make configurable HOSTNAMES = [ 'google.com', 'twitter.com', 'facebook.com', 'stackoverflow.com', 'yahoo.com', 'amazon.com', ] # TODO: Potentially make configurable NUMBER_OF_DNS_REQUESTS = 50 def __init__(self, devices, parameters): super().__init__(devices, parameters) self.dns_servers_before_connect = [] self.vpn_dns_servers = [] self.dns_helper = DNSHelper(self.localhost['dns_tool']) self.thread_pool = ThreadPool(processes=10) def dns_server_known(self, hostname): server = self.localhost['dns_tool'].lookup(hostname)[0] self.assertIsIn( server, self.dns_servers_before_connect, "DNS server used was {} but that wasn't known to the system".format(server)) def dns_server_is_vpn_dns(self, hostname): self.dns_helper.dns_server_is_vpn_server( self.dns_servers_before_connect, self.vpn_dns_servers, hostname=hostname) def check_multiple_asynchronously(self, func): results = [] # TODO: Think about what else we could do here. More domains? Different methods of DNS # lookup? for _ in range(0, TestDNSVanillaAggressive.NUMBER_OF_DNS_REQUESTS): # Warning: If you miss the trailing comma in the args to the func passed to apply_async # then the string will be interpreted as an array of arguments! results.append(self.thread_pool.apply_async( func, (random.choice(TestDNSVanillaAggressive.HOSTNAMES),))) # There is no result returned from check_dns, but .get() will propagate any exception # thrown by check_dns, which is what we want. first_exception = None for result in results: try: result.get() except XVEx as ex: if first_exception is None: first_exception = ex # pylint: disable=raising-bad-type if first_exception is not None: raise first_exception def test(self): L.describe('Find all known DNS servers before connecting to VPN') self.dns_servers_before_connect = self.localhost['dns_tool'].known_servers() L.info("All known DNS servers are: {}".format(self.dns_servers_before_connect)) # Sanity check that the DNS servers we initially use are known. This is not strictly part # of the test. self.check_multiple_asynchronously(self.dns_server_known) L.describe('Open and connect the VPN application') self.localhost['vpn_application'].open_and_connect() self.vpn_dns_servers = self.localhost['vpn_application'].dns_server_ips() L.info("VPN DNS servers are: {}".format(self.vpn_dns_servers)) L.describe( "Check DNS server used was a VPN DNS server by doing {} asynchronous DNS " "requests".format(TestDNSVanillaAggressive.NUMBER_OF_DNS_REQUESTS)) self.check_multiple_asynchronously(self.dns_server_is_vpn_dns)
def __init__(self, devices, parameters): super().__init__(devices, parameters) self.dns_servers_before_connect = [] self.vpn_dns_servers = [] self.dns_helper = DNSHelper(self.localhost['dns_tool']) self.thread_pool = ThreadPool(processes=10)
class DNSDuringDisruptionTestCase(LocalTestCase): DEFAULT_CHECK_PERIOD = 60 def __init__(self, disrupter_class, devices, parameters): super().__init__(devices, parameters) self.dns_helper = DNSHelper(self.localhost['dns_tool']) self._check_period = parameters.get( 'check_period', DNSDuringDisruptionTestCase.DEFAULT_CHECK_PERIOD) self.disrupter = disrupter_class(self.localhost, self.parameters) def setup(self): super().setup() self.disrupter.setup() def test(self): L.info("Using disrupter: {}".format(self.disrupter)) L.describe('Find all known DNS servers before connecting to VPN') dns_servers_before_connect = self.localhost['dns_tool'].known_servers() L.info( "All known DNS servers are: {}".format(dns_servers_before_connect)) L.describe('Open and connect the VPN application') self.localhost['vpn_application'].open_and_connect() vpn_dns_servers = self.localhost['vpn_application'].dns_server_ips() L.info("VPN DNS servers are: {}".format(vpn_dns_servers)) self._check_network(time_limit=20) L.describe('Check DNS server used was a VPN DNS server') self.dns_helper.dns_server_is_vpn_server(dns_servers_before_connect, vpn_dns_servers) L.describe("Create disruption...") self.disrupter.create_disruption() L.describe( "Do some DNS lookups for the next {} seconds and check the DNS server used is a VPN " "DNS Server".format(self._check_period)) timeup = TimeUp(self._check_period) message_time = self._check_period while not timeup: if timeup.time_left() < message_time: L.info("Doing DNS lookup tests for another {} seconds".format( math.ceil(timeup.time_left()))) message_time = message_time - 5 try: # Max timeout for each DNS lookup is 2 seconds. timeout = min(timeup.time_left(), 2) self.dns_helper.dns_server_is_vpn_server( dns_servers_before_connect, vpn_dns_servers, timeout=timeout) except XVEx as ex: L.warning("DNS lookup failed, assuming no leak: {}".format(ex)) def teardown(self): self.disrupter.teardown() super().teardown()
def __init__(self, disrupter_class, devices, parameters): super().__init__(devices, parameters) self.dns_helper = DNSHelper(self.localhost['dns_tool']) self._check_period = parameters.get( 'check_period', DNSDuringDisruptionTestCase.DEFAULT_CHECK_PERIOD) self.disrupter = disrupter_class(self.localhost, self.parameters)