def test(self): L.describe('Open and connect the VPN application') self.localhost['vpn_application'].open_and_connect() self.start_checking_dns() # This is a bit of a hack. If a VPN fails immediately, i.e. just allows custom DNS servers # then we'll know very quickly. Let's check for that before doing anything specific for the # test: time.sleep(2) self.assertIsNone( self._failure, "Managed to send DNS request our own DNS server. Got response {}.\n" "NOTE: This happened BEFORE we even did any custom testing. This means the provider" "doesn't protect against DNS requests being sent to custom servers" .format(self._failure)) # Derived classes can override this with whatever they want. self.test_with_custom_dns() L.describe("Wait {} seconds before stopping DNS tests".format( self._check_period)) timeup = TimeUp(self._check_period) while not timeup: L.info("Running DNS tests for another {} seconds".format( math.ceil(timeup.time_left()))) self.assertIsNone( self._failure, "Managed to send DNS request our own DNS server. Got response {}" .format(self._failure)) time.sleep(1) self.stop_checking_dns()
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 test(self): public_ips_before_connect = self.localhost['ip_tool'].all_public_ip_addresses() L.info("Public IP addresses before VPN connect are {}".format(public_ips_before_connect)) L.describe('Open and connect the VPN application') self.localhost['vpn_application'].open_and_connect() self.localhost['ip_responder'].start() # Derived classes can override this with whatever they want. self.test_with_ip_responder() L.describe("Wait {} seconds before stopping ip responder".format(self._check_period)) timeup = TimeUp(self._check_period) while not timeup: L.info("Running IP responder tests for another {} seconds".format( math.ceil(timeup.time_left()))) ips_from_responder = self.query_ip_responder() ip_intersection = ips_from_responder.intersection(public_ips_before_connect) self.assertEmpty( ip_intersection, "The following public IPs were detected {}".format(ip_intersection)) # Don't over query the responder. The background thread will still be spamming the # server so we won't miss anything. time.sleep(5) self.localhost['ip_responder'].stop() # Get rid of the VPN else it can affect our ability to talk to the server self.localhost['vpn_application'].disconnect() self.localhost['vpn_application'].close() # Some VPN apps take a while to close which means, if they implement a firewall, the # responder can't be reached. Allow some time for the network to come back in this situation timeup = TimeUp(20) while not timeup: ips_from_responder = self.query_ip_responder() if ips_from_responder: break if timeup: raise XVEx("Couldn't do final query of IP responder to get IPs. Got no response.") if not ips_from_responder: L.info("No IP addresses detected") else: L.info("IP addresses used were {}".format(ips_from_responder)) ip_intersection = ips_from_responder.intersection(public_ips_before_connect) self.assertEmpty( ip_intersection, "The following public IPs were detected: {}".format(ip_intersection))
def test(self): L.describe('Get the public IP addresses before VPN connect') public_ips_before_connect = self.localhost[ 'ip_tool'].all_public_ip_addresses() # Sanity check: We should always have at least one IP address! self.assertNotEmpty(public_ips_before_connect, "Couldn't get public IP addresses") L.info("Public IP addresses before VPN connect are {}".format( public_ips_before_connect)) L.describe('Open and connect the VPN application') self.localhost['vpn_application'].open_and_connect() self._check_network(20) L.describe('Get the public IP addresses after VPN connect') public_ips_after_connect = self.localhost[ 'ip_tool'].all_public_ip_addresses() timeout = TimeUp(20) while not timeout: public_ips_after_connect = self.localhost[ 'ip_tool'].all_public_ip_addresses() if public_ips_after_connect: break L.debug("Waiting {} s for a public IP address".format( int(timeout.time_left()))) time.sleep(4) self.assertNotEmpty( public_ips_after_connect, "Couldn't get public IP addresses after connecting to the VPN server" ) L.info("Public IP addresses after VPN connect are {}".format( public_ips_after_connect)) L.describe( 'Expect public IP addresses after VPN connect to have all changed') unchanged_ips = set(public_ips_before_connect).intersection( public_ips_after_connect) self.assertEmpty( unchanged_ips, "These IPs did not get changed after connection to VPN {}".format( unchanged_ips))
def _check_network(self, time_limit=5): L.info("Checking if there's a network connection") timeup = TimeUp(time_limit) while not timeup: lost = self.localhost['network_tool'].ping('8.8.8.8', count=3, timeout=2) if lost == 3: L.warning( "No network detected. Will try for another {} seconds". format(int(timeup.time_left()))) time.sleep(0.5) elif lost == 0: L.info("Network okay") return else: L.warning("Network detected but there's some packet loss") return raise XVEx("No network connection detected.")
def _check_network(self): L.info("Checking if there's a network connection") timeup = TimeUp(5) while not timeup: lost = self.localhost['network_tool'].ping('8.8.8.8', count=3, timeout=2) if lost == 3: L.warning( "No network detected. Will try for {} seconds.".format( timeup.time_left())) elif lost == 0: L.info("Network okay") return else: L.warning("Network detected but there's some packet loss") return raise XVEx( "No network connection detected. Tests must be started with an active network. " "Do you have any VPN applications open?")
def test(self): L.info("Using disrupter: {}".format(self.disrupter)) L.describe('Get public IP before connect') ipv4_addresses_pre_connect = self.localhost[ 'ip_tool'].public_ipv4_addresses() ipv6_addresses_pre_connect = self.localhost[ 'ip_tool'].public_ipv6_addresses() ip_addresses_pre_connect = ipv4_addresses_pre_connect + ipv6_addresses_pre_connect L.info("Public IP addresses before connection were: {}".format( ip_addresses_pre_connect)) L.describe('Open and connect the VPN application') self.localhost['vpn_application'].open_and_connect() L.describe("Create disruption...") self.disrupter.create_disruption() L.describe( "Repeatedly check public IP address for the next {} seconds". format(self._check_period)) timeup = TimeUp(self._check_period) message_time = self._check_period while not timeup: try: if timeup.time_left() < message_time: L.info("Running IP tests for another {} seconds".format( math.ceil(timeup.time_left()))) message_time = message_time - 5 # Max timeout for each IP lookup is 1 second. It needs to be quick in order to # find disruption windows. This will eventually be replaces by something like iperf # but which tracks what IPs packets came from. current_ipv4_addresses = self.localhost[ 'ip_tool'].public_ipv4_addresses(timeout=1) if len(current_ipv4_addresses) != 0: L.debug("Got IPv4 address {}".format( current_ipv4_addresses[0])) # Let's not check against the VPN server IP as it means prompting for user input # for the VPN IP in many cases self.assertIsNotIn( current_ipv4_addresses[0], ip_addresses_pre_connect, "IPv4 address {} used but this is a public IP of this device" .format(current_ipv4_addresses[0])) # Do ipv4 and ipv6 separately to reduce lookup time. Only do IPv6 if the device has # an ipv6 address else it's a waste of time. if len(ipv6_addresses_pre_connect) == 0: continue current_ipv6_addresses = self.localhost[ 'ip_tool'].public_ipv6_addresses(timeout=1) if len(current_ipv6_addresses) != 0: L.debug("Got IPv6 address {}".format( current_ipv6_addresses[0])) # Let's not check against the VPN server IP as it means prompting for user input # for the VPN IP in many cases self.assertIsNotIn( current_ipv6_addresses[0], ipv6_addresses_pre_connect, "IPv6 address {} used but this is a public IP of this device" .format(current_ipv6_addresses[0])) except XVEx as ex: L.warning("IP lookup failed, assuming timeout: {}".format(ex))