[tor-commits] [ooni-probe/master] Hardcoded vendor tests in separate function which optionally runs through setting in config, user-defined tests are optionally specified in asset file.
art at torproject.org
art at torproject.org
Mon Jul 9 14:39:04 UTC 2012
commit 6d57e84a19699a6f0dd2c6152ecafc87edacec9e
Author: Isis Lovecruft <isis at patternsinthevoid.net>
Date: Mon Apr 9 19:32:12 2012 -0700
Hardcoded vendor tests in separate function which optionally runs through setting in config, user-defined tests are optionally specified in asset file.
---
assets/captive_portal_tests.txt | 4 +
ooni-probe.conf | 13 ++-
tests/captiveportal.py | 208 ++++++++++++++++++++++++++++-----------
3 files changed, 166 insertions(+), 59 deletions(-)
diff --git a/assets/captive_portal_tests.txt b/assets/captive_portal_tests.txt
new file mode 100644
index 0000000..1bd016f
--- /dev/null
+++ b/assets/captive_portal_tests.txt
@@ -0,0 +1,4 @@
+
+http://ooni.nu, Open Observatory of Network Interference, 200
+http://www.patternsinthevoid.net/2CDB8B35pub.asc, mQINBE5qkHABEADVnasCm9w9hUff1E4iKnzcAdp4lx6XU5USmYdwKg2RQt2VFqWQ, 200
+http://www.google.com, Search the world's information, 200
diff --git a/ooni-probe.conf b/ooni-probe.conf
index a744fcf..a19c1d0 100644
--- a/ooni-probe.conf
+++ b/ooni-probe.conf
@@ -40,13 +40,20 @@ dns_reverse_lookup = true
### captiveportal testing configuration parameters
+# This is an optional list of user defined captive portal tests,
+# one per line, with each line in the format:
+# experiment_url, control_result, control_code
+# where experiment_url is the test page to retrieve,
+# control_result is some unique text found on the test page,
+# and control_code is the expected HTTP status code.
+captive_portal = captive_portal_tests.txt
+
# The default User Agent that ooni-probe should send for
# HTTP requests (pretend we're a Windows box running FF10):
default_ua = Mozilla/5.0 (Windows NT 6.1; WOW64; rv:10.0.2) Gecko/20100101 Firefox/10.0.2
-# This is the list of captive portal tests, in the format:
-# test_name, experiment_url, control_result, control_code
-captive_portal = captive_portal_tests.txt
+# Enable vendor tests for captive portals:
+do_captive_portal_vendor_tests = true
### traceroute testing related config parameters
diff --git a/tests/captiveportal.py b/tests/captiveportal.py
index 324d3cc..494a4c3 100644
--- a/tests/captiveportal.py
+++ b/tests/captiveportal.py
@@ -23,7 +23,7 @@ __desc__ = "Captive portal detection test"
class CaptivePortalAsset(Asset):
"""
- Parses captive_portal_test.txt into an Asset.
+ Parses captive_portal_tests.txt into an Asset.
"""
def __init__(self, file=None):
self = Asset.__init__(self, file)
@@ -32,28 +32,13 @@ class CaptivePortalAsset(Asset):
self = Asset.parse_line(self, line)
return line.replace('\n', '').split(', ')
- '''
- def next_asset(self):
- self = Asset.next_asset(self)
- with self.fh as fh:
- asset_list = []
- lines = fh.readlines()
- for line in lines:
- parsed_line = self.parse_line(line)
- if parsed_line:
- asset_list.append(parsed_line)
- else:
- fh.seek(0)
- raise StopIteration
- return asset_list
- '''
-
class CaptivePortal(Test):
"""
Compares content and status codes of HTTP responses, and attempts
to determine if content has been altered.
- TODO: compare headers
+ TODO: compare headers, random URL requests with control obtained
+ through Tor.
"""
def __init__(self, ooni):
Test.__init__(self, ooni, name='test')
@@ -70,7 +55,7 @@ class CaptivePortal(Test):
return response
def http_content_match_fuzzy_opt(self, experimental_url, control_result,
- fuzzy=False):
+ headers=None, fuzzy=False):
"""
Makes an HTTP request on port 80 for experimental_url, then
compares the response_content of experimental_url with the
@@ -78,13 +63,15 @@ class CaptivePortal(Test):
True, the response_content is compared with a regex of the
control_result. If the response_content from the
experimental_url and the control_result match, returns True
- with the HTTP status code, False if otherwise.
+ with the HTTP status code, False and status code if otherwise.
"""
log = self.logger
- default_ua = self.default_ua
- response = self.http_fetch(experimental_url,
- headers={'User-Agent': default_ua})
+ if headers is None:
+ default_ua = self.default_ua
+ headers = {'User-Agent': default_ua}
+
+ response = self.http_fetch(experimental_url, headers)
response_content = response.read()
response_code = response.code
if response_content is not None:
@@ -129,6 +116,89 @@ class CaptivePortal(Test):
return False
return True
+ def run_vendor_tests(self, *a, **kw):
+ """
+ These are several vendor tests used to detect the presence of
+ a captive portal. Each test compares HTTP status code and
+ content to the control results and has its own User-Agent
+ string, in order to emulate the test as it would occur on the
+ device it was intended for. Vendor tests are defined in the
+ format:
+ [experimental_url, control_response, control_code, ua, test_name]
+ """
+ cm = self.http_content_match_fuzzy_opt
+ sm = self.http_status_code_match
+ snm = self.http_status_code_no_match
+
+ log = self.logger
+
+ vendor_tests = [['http://www.apple.com/library/test/success.html',
+ 'Success',
+ '200',
+ 'Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A543a Safari/419.3',
+ 'Apple HTTP Captive Portal'],
+ ['http://tools.ietf.org/html/draft-nottingham-http-portal-02',
+ '428 Network Authentication Required',
+ '428',
+ 'Mozilla/5.0 (Windows NT 6.1; rv:5.0) Gecko/20100101 Firefox/5.0',
+ 'W3 Captive Portal'],
+ ['http://www.msftncsi.com/ncsi.txt',
+ 'Microsoft NCSI',
+ '200',
+ 'Microsoft NCSI',
+ 'MS HTTP Captive Portal',]]
+
+ log.debug("Getting vendor test data")
+
+ for vt in vendor_tests:
+ experiment_url = vt[0]
+ control_result = vt[1]
+ control_code = vt[2]
+ ua = vt[3]
+ test_name = vt[4]
+
+ if test_name == "MS HTTP Captive Portal":
+ log.info("Running the %s test..." % test_name)
+ content_match, experiment_code = cm(experiment_url, control_result,
+ headers={'User-Agent': ua})
+ status_match = sm(experiment_code, control_code)
+ if status_match and content_match:
+ log.info("The %s test was unable to detect a captive portal."
+ % test_name)
+ else:
+ log.info("The %s test shows that your network is filtered."
+ % test_name)
+
+ elif test_name == "Apple HTTP Captive Portal":
+ log.info("Running the %s test..." % test_name)
+ content_fuzzy_match, experiment_code = cm(experiment_url,
+ control_result,
+ headers={'User-Agent': ua},
+ fuzzy=True)
+ status_match = sm(experiment_code, control_code)
+ if status_match and content_fuzzy_match:
+ log.info("The %s test was unable to detect a captive portal."
+ % test_name)
+ else:
+ log.info("The %s test shows that your network is filtered."
+ % test_name)
+
+ elif test_name == "W3 Captive Portal":
+ log.info("Running the %s test..." % test_name)
+ content_fuzzy_match, experiment_code = cm(experiment_url,
+ control_result,
+ headers={'User-Agent': ua},
+ fuzzy=True)
+ status_no_match = snm(experiment_code, control_code)
+ if status_no_match and content_fuzzy_match:
+ log.info("The %s test was unable to detect a captive portal."
+ % test_name)
+ else:
+ log.info("The %s test shows that your network is filtered."
+ % test_name)
+ else:
+ log.warn("Ooni is trying to run an undefined CP vendor test.")
+
def experiment(self, *a, **kw):
"""
Compares the content and status code of the HTTP response for
@@ -139,53 +209,79 @@ class CaptivePortal(Test):
in the experimental content. Returns True if matches are found,
and False if otherwise.
"""
- test_name = kw['data'][0]
- experiment_url = kw['data'][1]
- control_result = kw['data'][2]
- control_code = kw['data'][3]
-
+ if (os.path.isfile(os.path.join(self.config.main.assetdir,
+ self.config.tests.captive_portal))):
+ kw['data'].append(None)
+ kw['data'].append('user defined')
+
+ experiment_url = kw['data'][0]
+ control_result = kw['data'][1]
+ control_code = kw['data'][2]
+ ua = kw['data'][3]
+ test_name = kw['data'][4]
+
cm = self.http_content_match_fuzzy_opt
sm = self.http_status_code_match
-
+ snm = self.http_status_code_no_match
+
log = self.logger
- log.info("Running the %s test..." % test_name)
- content_match, experiment_code = cm(experiment_url, control_result)
- status_match = sm(experiment_code, control_code)
-
- if status_match and content_match:
- log.info("The %s test was unable to detect a captive portal."
- % test_name)
- return True
- elif status_match and not content_match:
- log.info("The %s test detected mismatched content, retrying with " \
- "fuzzy match enabled." % test_name)
- content_fuzzy_match, experiment_code = cm(experiment_url,
- control_result,
- fuzzy=True)
- if content_fuzzy_match:
- return True
+ if test_name == "user defined":
+ log.info("Running the %s test for %s..." % (test_name, experiment_url))
+ content_match, experiment_code = cm(experiment_url, control_result)
+ status_match = sm(experiment_code, control_code)
+ if status_match and content_match:
+ log.info("The %s test was unable to detect a captive portal."
+ % test_name)
+ return True, test_name
+ elif status_match and not content_match:
+ log.info("The %s test detected mismatched content, retrying "
+ "with fuzzy match enabled." % test_name)
+ content_fuzzy_match, experiment_code = cm(experiment_url,
+ control_result,
+ fuzzy=True)
+ if content_fuzzy_match:
+ return True, test_name
+ else:
+ return False, test_name
else:
- return False
+ log.info("The %s test shows that your network is filtered."
+ % test_name)
+ return False, test_name
+
else:
- log.info("The %s test shows that your network is filtered, possibly " \
- "due to a captive portal." % test_name)
- return False
-
- return False
+ log.warn("Ooni is trying to run an undefined captive portal test.")
+ return False, test_name
+
def run(ooni):
"""
- Run the CaptivePortal(Test).
+ Runs the CaptivePortal(Test).
+
+ If do_captive_portal_vendor_tests is set to true, then vendor
+ specific captive portal tests will be run.
+
+ If captive_portal = filename.txt, then user-specified tests
+ will be run.
+
+ Either vendor tests or user-defined tests can be run, or both.
"""
config = ooni.config
log = ooni.logger
- assets = [CaptivePortalAsset(os.path.join(config.main.assetdir,
- config.tests.captive_portal))]
+ assets = []
+ if (os.path.isfile(os.path.join(config.main.assetdir,
+ config.tests.captive_portal))):
+ assets.append(CaptivePortalAsset(os.path.join(config.main.assetdir,
+ config.tests.captive_portal)))
+
captiveportal = CaptivePortal(ooni)
log.info("Starting captive portal test...")
+ log.info("Running user defined tests...")
captiveportal.run(assets, {'index': 1})
- log.info("Captive portal test finished!")
-
+
+ if config.tests.do_captive_portal_vendor_tests:
+ log.info("Running vendor tests...")
+ captiveportal.run_vendor_tests()
+ log.info("Captive portal test finished!")
More information about the tor-commits
mailing list