commit 4d2a344a05ac4db2aa551d685e277748b87f40b3 Author: Arturo Filastò arturo@filasto.net Date: Thu Sep 22 18:12:04 2016 +0200
First pass at implementing test for Facebook Messenger --- ooni/nettests/blocking/facebook_messenger.py | 151 +++++++++++++++++++++++++++ 1 file changed, 151 insertions(+)
diff --git a/ooni/nettests/blocking/facebook_messenger.py b/ooni/nettests/blocking/facebook_messenger.py new file mode 100644 index 0000000..787d7ad --- /dev/null +++ b/ooni/nettests/blocking/facebook_messenger.py @@ -0,0 +1,151 @@ +# -*- encoding: utf-8 -*- + +from twisted.internet import defer, reactor +from twisted.python import usage +from twisted.internet.endpoints import TCP4ClientEndpoint + +from ooni.geoip import ip_to_location +from ooni.utils import log +from ooni.common.tcp_utils import TCPConnectFactory +from ooni.errors import failureToString + +from ooni.templates import httpt, dnst + + +class UsageOptions(usage.Options): + pass + + +FB_HOSTNAMES = { + 'stun': "stun.fbsbx.com", + 'b-api': "b-api.facebook.com", + 'b-graph': "b-graph.facebook.com", + 'edge': "edge-mqtt.facebook.com", + 'external-cdn': "external.xx.fbcdn.net", + 'scontent-cdn': "scontent.xx.fbcdn.net", + 'star': "star.c10r.facebook.com" +} + +def is_facebook_ip(ip_address): + """ + :return: True when the IP in questions belongs to the facebook ASN + """ + try: + location = ip_to_location(ip_address) + return location['asn'] == 'AS32934' + except: + return False + +class FacebookMessengerTest(httpt.HTTPTest, dnst.DNSTest): + name = "Facebook Messenger" + description = ("This test checks to see if the servers used by Facebook " + "messenger are reachable") + author = "Arturo Filastò" + version = "0.1.0" + + requiresRoot = False + requiresTor = False + followRedirects = True + usageOptions = UsageOptions + + def setUp(self): + for key in FB_HOSTNAMES.keys(): + self.report['facebook-{0}-dns-consistent'.format(key)] = None + self.report['facebook-{0}-reachable'.format(key)] = None + + self.report['facebook-tcp-blocking'] = None + self.report['facebook-dns-blocking'] = None + self.report['tcp_connect'] = [] + + def _test_connect_to_port(self, address, port): + result = { + 'ip': address, + 'port': port, + 'status': { + 'success': None, + 'failure': None + } + } + point = TCP4ClientEndpoint(reactor, address, port, timeout=10) + d = point.connect(TCPConnectFactory()) + @d.addCallback + def cb(p): + result['status']['success'] = True + result['status']['failure'] = False + self.report['tcp_connect'].append(result) + + @d.addErrback + def eb(failure): + result['status']['success'] = False + result['status']['failure'] = failureToString(failure) + self.report['tcp_connect'].append(result) + + return d + + def _test_tcp_connect(self, consistent_addresses): + for key, addresses in consistent_addresses.items(): + if key == 'stun': + # XXX we currently don't test stun + continue + + dl = [] + for address in addresses: + dl.append(self._test_connect_to_port(address, 443)) + results = yield defer.DeferredList(dl, consumeErrors=True) + tcp_blocked = False + for success, result in results: + if success == False: + tcp_blocked = True + + if tcp_blocked == True: + log.msg("{0} server is blocked based on TCP".format(key)) + self.report['facebook-{0}-reachable'.format(key)] = not tcp_blocked + + @defer.inlineCallbacks + def _test_dns_resolution(self): + consistent_addresses = {} + for key, hostname in FB_HOSTNAMES.items(): + consistent_addresses[key] = [] + consistent = False + try: + addresses = yield self.performALookup(hostname) + for address in addresses: + if is_facebook_ip(address): + consistent = True + consistent_addresses[key].append(address) + except Exception: + log.err("Failed to lookup {0}: {1}".format(key, hostname)) + finally: + msg = "{0}: {1} appears to present ".format(key, hostname) + if consistent == True: + msg += "consistent DNS" + else: + msg += "inconsistent DNS" + log.msg(msg) + self.report['facebook-{0}-dns-consistent'.format(key)] = consistent + + defer.returnValue(consistent_addresses) + + @defer.inlineCallbacks + def test_endpoints(self): + consistent_addresses = yield self._test_dns_resolution() + yield self._test_tcp_connect(consistent_addresses) + dns_blocking = False + tcp_blocking = False + for key in FB_HOSTNAMES.keys(): + if self.report['facebook-{0}-dns-consistent'.format(key)] == False: + dns_blocking = True + log.msg("{0} is blocked due to DNS blocking".format(key)) + continue + + # XXX We ignore stun reachability as it requires UDP + if key == 'stun': + continue + if self.report['facebook-{0}-reachable'.format(key)] == False: + tcp_blocking = True + log.msg("{0} is blocked due to TCP/IP blocking".format(key)) + continue + log.msg("{0} no blocking detected".format(key)) + + self.report['facebook-tcp-blocking'] = tcp_blocking + self.report['facebook-dns-blocking'] = dns_blocking