ooni-probe-cli/QA/webconnectivity.py
Simone Basso 7df25795c0
fix(probeservices): use api.ooni.io (#926)
See https://github.com/ooni/probe/issues/2147.

Note that this PR also tries to reduce usage of legacy names inside unit/integration tests.
2022-09-02 16:48:14 +02:00

889 lines
33 KiB
Python
Executable File

#!/usr/bin/env python3
""" ./QA/webconnectivity.py - main QA script for webconnectivity
This script performs a bunch of webconnectivity tests under censored
network conditions and verifies that the measurement is consistent
with the expectations, by parsing the resulting JSONL. """
import socket
import sys
import time
sys.path.insert(0, ".")
import common
def execute_jafar_and_return_validated_test_keys(
ooni_exe, outfile, experiment_args, tag, args
):
"""Executes jafar and returns the validated parsed test keys, or throws
an AssertionError if the result is not valid."""
tk = common.execute_jafar_and_miniooni(
ooni_exe, outfile, experiment_args, tag, args
)
print("dns_experiment_failure", tk["dns_experiment_failure"], file=sys.stderr)
print("dns_consistency", tk["dns_consistency"], file=sys.stderr)
print("control_failure", tk["control_failure"], file=sys.stderr)
print("http_experiment_failure", tk["http_experiment_failure"], file=sys.stderr)
print("tk_body_length_match", tk["body_length_match"], file=sys.stderr)
print("body_proportion", tk["body_proportion"], file=sys.stderr)
print("status_code_match", tk["status_code_match"], file=sys.stderr)
print("headers_match", tk["headers_match"], file=sys.stderr)
print("title_match", tk["title_match"], file=sys.stderr)
print("blocking", tk["blocking"], file=sys.stderr)
print("accessible", tk["accessible"], file=sys.stderr)
print("x_status", tk["x_status"], file=sys.stderr)
return tk
def assert_status_flags_are(ooni_exe, tk, desired):
"""Checks whether the status flags are what we expect them to
be when we're running miniooni. This check only makes sense
with miniooni b/c status flags are a miniooni extension."""
if "miniooni" not in ooni_exe:
return
assert tk["x_status"] == desired
def webconnectivity_https_ok_with_control_failure(ooni_exe, outfile):
"""Successful HTTPS measurement but control failure."""
args = [
"-iptables-reset-keyword",
"th.ooni.org",
]
tk = execute_jafar_and_return_validated_test_keys(
ooni_exe,
outfile,
"-i https://example.com/ web_connectivity",
"webconnectivity_https_ok_with_control_failure",
args,
)
assert tk["dns_experiment_failure"] == None
assert tk["dns_consistency"] == None
assert tk["control_failure"] == "connection_reset"
assert tk["http_experiment_failure"] == None
assert tk["body_length_match"] == None
assert tk["body_proportion"] == 0
assert tk["status_code_match"] == None
assert tk["headers_match"] == None
assert tk["title_match"] == None
if "miniooni" in ooni_exe:
assert tk["blocking"] == False
assert tk["accessible"] == True
else:
assert tk["blocking"] == None
assert tk["accessible"] == None
assert_status_flags_are(ooni_exe, tk, 1)
def webconnectivity_http_ok_with_control_failure(ooni_exe, outfile):
"""Successful HTTP measurement but control failure."""
args = [
"-iptables-reset-keyword",
"th.ooni.org",
]
tk = execute_jafar_and_return_validated_test_keys(
ooni_exe,
outfile,
"-i http://example.org/ web_connectivity",
"webconnectivity_http_ok_with_control_failure",
args,
)
assert tk["dns_experiment_failure"] == None
assert tk["dns_consistency"] == None
assert tk["control_failure"] == "connection_reset"
assert tk["http_experiment_failure"] == None
assert tk["body_length_match"] == None
assert tk["body_proportion"] == 0
assert tk["status_code_match"] == None
assert tk["headers_match"] == None
assert tk["title_match"] == None
assert tk["blocking"] == None
assert tk["accessible"] == None
assert_status_flags_are(ooni_exe, tk, 8)
def webconnectivity_transparent_http_proxy(ooni_exe, outfile):
"""Test case where we pass through a transparent HTTP proxy"""
args = []
args.append("-iptables-hijack-http-to")
args.append("127.0.0.1:80")
tk = execute_jafar_and_return_validated_test_keys(
ooni_exe,
outfile,
"-i http://example.org web_connectivity",
"webconnectivity_transparent_http_proxy",
args,
)
assert tk["dns_experiment_failure"] == None
assert tk["dns_consistency"] == "consistent"
assert tk["control_failure"] == None
assert tk["http_experiment_failure"] == None
assert tk["body_length_match"] == True
assert tk["body_proportion"] == 1
assert tk["status_code_match"] == True
assert tk["headers_match"] == True
assert tk["title_match"] == True
assert tk["blocking"] == False
assert tk["accessible"] == True
assert_status_flags_are(ooni_exe, tk, 2)
def webconnectivity_transparent_https_proxy(ooni_exe, outfile):
"""Test case where we pass through a transparent HTTPS proxy"""
args = []
args.append("-iptables-hijack-https-to")
args.append("127.0.0.1:443")
tk = execute_jafar_and_return_validated_test_keys(
ooni_exe,
outfile,
"-i https://example.org web_connectivity",
"webconnectivity_transparent_https_proxy",
args,
)
assert tk["dns_experiment_failure"] == None
assert tk["dns_consistency"] == "consistent"
assert tk["control_failure"] == None
assert tk["http_experiment_failure"] == None
assert tk["body_length_match"] == True
assert tk["body_proportion"] == 1
assert tk["status_code_match"] == True
assert tk["headers_match"] == True
assert tk["title_match"] == True
assert tk["blocking"] == False
assert tk["accessible"] == True
assert_status_flags_are(ooni_exe, tk, 1)
def webconnectivity_dns_hijacking(ooni_exe, outfile):
"""Test case where there is DNS hijacking towards a transparent proxy."""
args = []
args.append("-iptables-hijack-dns-to")
args.append("127.0.0.1:53")
args.append("-dns-proxy-hijack")
args.append("example.org")
tk = execute_jafar_and_return_validated_test_keys(
ooni_exe,
outfile,
"-i https://example.org web_connectivity",
"webconnectivity_dns_hijacking",
args,
)
assert tk["dns_experiment_failure"] == None
assert tk["dns_consistency"] == "inconsistent"
assert tk["control_failure"] == None
assert tk["http_experiment_failure"] == None
assert tk["body_length_match"] == True
assert tk["body_proportion"] == 1
assert tk["status_code_match"] == True
assert tk["headers_match"] == True
assert tk["title_match"] == True
assert tk["blocking"] == False
assert tk["accessible"] == True
assert_status_flags_are(ooni_exe, tk, 1)
def webconnectivity_control_unreachable_and_using_http(ooni_exe, outfile):
"""Test case where the control is unreachable and we're using the
plaintext HTTP protocol rather than HTTPS"""
args = []
args.append("-iptables-reset-keyword")
args.append("th.ooni.org")
tk = execute_jafar_and_return_validated_test_keys(
ooni_exe,
outfile,
"-i http://example.org web_connectivity",
"webconnectivity_control_unreachable_and_using_http",
args,
)
assert tk["dns_experiment_failure"] == None
assert tk["dns_consistency"] == None
assert tk["control_failure"] == "connection_reset"
assert tk["http_experiment_failure"] == None
assert tk["body_length_match"] == None
assert tk["body_proportion"] == 0
assert tk["status_code_match"] == None
assert tk["headers_match"] == None
assert tk["title_match"] == None
assert tk["blocking"] == None
assert tk["accessible"] == None
assert_status_flags_are(ooni_exe, tk, 8)
def webconnectivity_nonexistent_domain(ooni_exe, outfile):
"""Test case where the domain does not exist"""
args = []
tk = execute_jafar_and_return_validated_test_keys(
ooni_exe,
outfile,
"-i http://www.ooni.nonexistent web_connectivity",
"webconnectivity_nonexistent_domain",
args,
)
# TODO(bassosimone): Debateable result. We need to do better here.
# See <https://github.com/ooni/probe-engine/issues/579>.
#
# Note that MK is not doing it right here because it's suppressing the
# dns_nxdomain_error that instead is very informative. Yet, it is reporting
# a failure in HTTP, which miniooni does not because it does not make
# sense to perform HTTP when there are no IP addresses.
#
# The following seems indeed a bug in MK where we don't properly record the
# actual error that occurred when performing the DNS experiment.
#
# See <https://github.com/measurement-kit/measurement-kit/issues/1931>.
if "miniooni" in ooni_exe:
assert tk["dns_experiment_failure"] == "dns_nxdomain_error"
else:
assert tk["dns_experiment_failure"] == None
assert tk["dns_consistency"] == "consistent"
assert tk["control_failure"] == None
if "miniooni" in ooni_exe:
assert tk["http_experiment_failure"] == None
else:
assert tk["http_experiment_failure"] == "dns_lookup_error"
assert tk["body_length_match"] == None
assert tk["body_proportion"] == 0
assert tk["status_code_match"] == None
assert tk["headers_match"] == None
assert tk["title_match"] == None
assert tk["blocking"] == False
assert tk["accessible"] == True
assert_status_flags_are(ooni_exe, tk, 2052)
def webconnectivity_tcpip_blocking_with_consistent_dns(ooni_exe, outfile):
"""Test case where there's TCP/IP blocking w/ consistent DNS"""
ip = socket.gethostbyname("nexa.polito.it")
args = [
"-iptables-drop-ip",
ip,
]
tk = execute_jafar_and_return_validated_test_keys(
ooni_exe,
outfile,
"-i http://nexa.polito.it web_connectivity",
"webconnectivity_tcpip_blocking_with_consistent_dns",
args,
)
assert tk["dns_experiment_failure"] == None
assert tk["dns_consistency"] == "consistent"
assert tk["control_failure"] == None
assert tk["http_experiment_failure"] == "generic_timeout_error"
assert tk["body_length_match"] == None
assert tk["body_proportion"] == 0
assert tk["status_code_match"] == None
assert tk["headers_match"] == None
assert tk["title_match"] == None
assert tk["blocking"] == "tcp_ip"
assert tk["accessible"] == False
assert_status_flags_are(ooni_exe, tk, 4224)
def webconnectivity_tcpip_blocking_with_inconsistent_dns(ooni_exe, outfile):
"""Test case where there's TCP/IP blocking w/ inconsistent DNS"""
def runner(port):
args = [
"-dns-proxy-hijack",
"nexa.polito.it",
"-iptables-hijack-dns-to",
"127.0.0.1:53",
"-iptables-hijack-http-to",
"127.0.0.1:{}".format(port),
]
tk = execute_jafar_and_return_validated_test_keys(
ooni_exe,
outfile,
"-i http://nexa.polito.it web_connectivity",
"webconnectivity_tcpip_blocking_with_inconsistent_dns",
args,
)
assert tk["dns_experiment_failure"] == None
assert tk["dns_consistency"] == "inconsistent"
assert tk["control_failure"] == None
assert tk["http_experiment_failure"] == "connection_refused"
assert tk["body_length_match"] == None
assert tk["body_proportion"] == 0
assert tk["status_code_match"] == None
assert tk["headers_match"] == None
assert tk["title_match"] == None
assert tk["blocking"] == "dns"
assert tk["accessible"] == False
assert_status_flags_are(ooni_exe, tk, 4256)
common.with_free_port(runner)
def webconnectivity_http_connection_refused_with_consistent_dns(ooni_exe, outfile):
"""Test case where there's TCP/IP blocking w/ consistent DNS that occurs
while we're following the chain of redirects."""
# We use a bit.ly link redirecting to nexa.polito.it. We block the IP address
# used by nexa.polito.it. So the error should happen in the redirect chain.
ip = socket.gethostbyname("nexa.polito.it")
args = [
"-iptables-reset-ip",
ip,
]
tk = execute_jafar_and_return_validated_test_keys(
ooni_exe,
outfile,
"-i https://bit.ly/3h9EJR3 web_connectivity",
"webconnectivity_http_connection_refused_with_consistent_dns",
args,
)
assert tk["dns_experiment_failure"] == None
assert tk["dns_consistency"] == "consistent"
assert tk["control_failure"] == None
assert tk["http_experiment_failure"] == "connection_refused"
assert tk["body_length_match"] == None
assert tk["body_proportion"] == 0
assert tk["status_code_match"] == None
assert tk["headers_match"] == None
assert tk["title_match"] == None
assert tk["blocking"] == "http-failure"
assert tk["accessible"] == False
assert_status_flags_are(ooni_exe, tk, 8320)
def webconnectivity_http_connection_reset_with_consistent_dns(ooni_exe, outfile):
"""Test case where there's RST-based blocking blocking w/ consistent DNS that
occurs while we're following the chain of redirects."""
# We use a bit.ly link redirecting to nexa.polito.it. We block the Host header
# used for nexa.polito.it. So the error should happen in the redirect chain.
args = [
"-iptables-reset-keyword",
"Host: nexa",
]
tk = execute_jafar_and_return_validated_test_keys(
ooni_exe,
outfile,
"-i https://bit.ly/3h9EJR3 web_connectivity",
"webconnectivity_http_connection_reset_with_consistent_dns",
args,
)
assert tk["dns_experiment_failure"] == None
assert tk["dns_consistency"] == "consistent"
assert tk["control_failure"] == None
assert tk["http_experiment_failure"] == "connection_reset"
assert tk["body_length_match"] == None
assert tk["body_proportion"] == 0
assert tk["status_code_match"] == None
assert tk["headers_match"] == None
assert tk["title_match"] == None
assert tk["blocking"] == "http-failure"
assert tk["accessible"] == False
assert_status_flags_are(ooni_exe, tk, 8448)
def webconnectivity_http_nxdomain_with_consistent_dns(ooni_exe, outfile):
"""Test case where there's a redirection and the redirected request cannot
continue because a NXDOMAIN error occurs."""
# We use a bit.ly link redirecting to nexa.polito.it. We block the DNS request
# for nexa.polito.it. So the error should happen in the redirect chain.
args = [
"-iptables-hijack-dns-to",
"127.0.0.1:53",
"-dns-proxy-block",
"nexa.polito.it",
]
tk = execute_jafar_and_return_validated_test_keys(
ooni_exe,
outfile,
"-i https://bit.ly/3h9EJR3 web_connectivity",
"webconnectivity_http_nxdomain_with_consistent_dns",
args,
)
assert tk["dns_experiment_failure"] == None
assert tk["dns_consistency"] == "consistent"
assert tk["control_failure"] == None
assert (
tk["http_experiment_failure"] == "dns_nxdomain_error" # miniooni
or tk["http_experiment_failure"] == "dns_lookup_error" # MK
)
assert tk["body_length_match"] == None
assert tk["body_proportion"] == 0
assert tk["status_code_match"] == None
assert tk["headers_match"] == None
assert tk["title_match"] == None
assert tk["blocking"] == "dns"
assert tk["accessible"] == False
assert_status_flags_are(ooni_exe, tk, 8224)
def webconnectivity_http_eof_error_with_consistent_dns(ooni_exe, outfile):
"""Test case where there's a redirection and the redirected request cannot
continue because an eof_error error occurs."""
# We use a bit.ly link redirecting to nexa.polito.it. We block the HTTP request
# for nexa.polito.it using the cleartext bad proxy. So the error should happen in
# the redirect chain and should be EOF.
args = [
"-iptables-hijack-dns-to",
"127.0.0.1:53",
"-dns-proxy-hijack",
"nexa.polito.it",
"-iptables-hijack-http-to",
"127.0.0.1:7117", # this is badproxy's cleartext endpoint
]
tk = execute_jafar_and_return_validated_test_keys(
ooni_exe,
outfile,
"-i https://bit.ly/3h9EJR3 web_connectivity", # bit.ly uses https
"webconnectivity_http_eof_error_with_consistent_dns",
args,
)
assert tk["dns_experiment_failure"] == None
assert tk["dns_consistency"] == "consistent"
assert tk["control_failure"] == None
assert tk["http_experiment_failure"] == "eof_error"
assert tk["body_length_match"] == None
assert tk["body_proportion"] == 0
assert tk["status_code_match"] == None
assert tk["headers_match"] == None
assert tk["title_match"] == None
assert tk["blocking"] == "http-failure"
assert tk["accessible"] == False
assert_status_flags_are(ooni_exe, tk, 8448)
def webconnectivity_http_generic_timeout_error_with_consistent_dns(ooni_exe, outfile):
"""Test case where there's a redirection and the redirected request cannot
continue because a generic_timeout_error error occurs."""
# We use a bit.ly link redirecting to nexa.polito.it. We block the HTTP request
# for nexa.polito.it by dropping packets using DPI. So the error should happen in
# the redirect chain and should be timeout.
args = [
"-iptables-hijack-dns-to",
"127.0.0.1:53",
"-dns-proxy-hijack",
"nexa.polito.it",
"-iptables-drop-keyword",
"Host: nexa",
]
tk = execute_jafar_and_return_validated_test_keys(
ooni_exe,
outfile,
"-i https://bit.ly/3h9EJR3 web_connectivity",
"webconnectivity_http_generic_timeout_error_with_consistent_dns",
args,
)
assert tk["dns_experiment_failure"] == None
assert tk["dns_consistency"] == "consistent"
assert tk["control_failure"] == None
assert tk["http_experiment_failure"] == "generic_timeout_error"
assert tk["body_length_match"] == None
assert tk["body_proportion"] == 0
assert tk["status_code_match"] == None
assert tk["headers_match"] == None
assert tk["title_match"] == None
assert tk["blocking"] == "http-failure"
assert tk["accessible"] == False
assert_status_flags_are(ooni_exe, tk, 8704)
def webconnectivity_http_connection_reset_with_inconsistent_dns(ooni_exe, outfile):
"""Test case where there's inconsistent DNS and the connection is RST when
we're executing HTTP code."""
args = [
"-iptables-reset-keyword",
"nexa.polito.it",
"-iptables-hijack-dns-to",
"127.0.0.1:53",
"-dns-proxy-hijack",
"polito",
]
tk = execute_jafar_and_return_validated_test_keys(
ooni_exe,
outfile,
"-i http://nexa.polito.it/ web_connectivity",
"webconnectivity_http_connection_reset_with_inconsistent_dns",
args,
)
assert tk["dns_experiment_failure"] == None
assert tk["dns_consistency"] == "inconsistent"
assert tk["control_failure"] == None
assert tk["http_experiment_failure"] == "connection_reset"
assert tk["body_length_match"] == None
assert tk["body_proportion"] == 0
assert tk["status_code_match"] == None
assert tk["headers_match"] == None
assert tk["title_match"] == None
assert tk["blocking"] == "dns"
assert tk["accessible"] == False
assert_status_flags_are(ooni_exe, tk, 8480)
def webconnectivity_http_successful_website(ooni_exe, outfile):
"""Test case where we succeed with an HTTP only webpage"""
args = []
tk = execute_jafar_and_return_validated_test_keys(
ooni_exe,
outfile,
"-i http://example.org/ web_connectivity",
"webconnectivity_http_successful_website",
args,
)
assert tk["dns_experiment_failure"] == None
assert tk["dns_consistency"] == "consistent"
assert tk["control_failure"] == None
assert tk["http_experiment_failure"] == None
assert tk["body_length_match"] == True
assert tk["body_proportion"] == 1
assert tk["status_code_match"] == True
assert tk["headers_match"] == True
assert tk["title_match"] == True
assert tk["blocking"] == False
assert tk["accessible"] == True
assert_status_flags_are(ooni_exe, tk, 2)
def webconnectivity_https_successful_website(ooni_exe, outfile):
"""Test case where we succeed with an HTTPS only webpage"""
args = []
tk = execute_jafar_and_return_validated_test_keys(
ooni_exe,
outfile,
"-i https://example.com/ web_connectivity",
"webconnectivity_https_successful_website",
args,
)
assert tk["dns_experiment_failure"] == None
assert tk["dns_consistency"] == "consistent"
assert tk["control_failure"] == None
assert tk["http_experiment_failure"] == None
assert tk["body_length_match"] == True
assert tk["body_proportion"] == 1
assert tk["status_code_match"] == True
assert tk["headers_match"] == True
assert tk["title_match"] == True
assert tk["blocking"] == False
assert tk["accessible"] == True
assert_status_flags_are(ooni_exe, tk, 1)
def webconnectivity_http_diff_with_inconsistent_dns(ooni_exe, outfile):
"""Test case where we get an http-diff and the DNS is inconsistent"""
args = [
"-iptables-hijack-dns-to",
"127.0.0.1:53",
"-dns-proxy-hijack",
"example.org",
"-http-proxy-block",
"example.org",
]
tk = execute_jafar_and_return_validated_test_keys(
ooni_exe,
outfile,
"-i http://example.org/ web_connectivity",
"webconnectivity_http_diff_with_inconsistent_dns",
args,
)
assert tk["dns_experiment_failure"] == None
assert tk["dns_consistency"] == "inconsistent"
assert tk["control_failure"] == None
assert tk["http_experiment_failure"] == None
assert tk["body_length_match"] == False
assert tk["body_proportion"] < 1
assert tk["status_code_match"] == False
assert tk["headers_match"] == True
assert tk["title_match"] == False
assert tk["blocking"] == "dns"
assert tk["accessible"] == False
assert_status_flags_are(ooni_exe, tk, 96)
def webconnectivity_http_diff_with_consistent_dns(ooni_exe, outfile):
"""Test case where we get an http-diff and the DNS is consistent"""
args = [
"-iptables-hijack-http-to",
"127.0.0.1:80",
"-http-proxy-block",
"example.org",
]
tk = execute_jafar_and_return_validated_test_keys(
ooni_exe,
outfile,
"-i http://example.org/ web_connectivity",
"webconnectivity_http_diff_with_consistent_dns",
args,
)
assert tk["dns_experiment_failure"] == None
assert tk["dns_consistency"] == "consistent"
assert tk["control_failure"] == None
assert tk["http_experiment_failure"] == None
assert tk["body_length_match"] == False
assert tk["body_proportion"] < 1
assert tk["status_code_match"] == False
assert tk["headers_match"] == True
assert tk["title_match"] == False
assert tk["blocking"] == "http-diff"
assert tk["accessible"] == False
assert_status_flags_are(ooni_exe, tk, 64)
def webconnectivity_https_expired_certificate(ooni_exe, outfile):
"""Test case where the domain's certificate is expired"""
args = []
tk = execute_jafar_and_return_validated_test_keys(
ooni_exe,
outfile,
"-i https://expired.badssl.com/ web_connectivity",
"webconnectivity_https_expired_certificate",
args,
)
assert tk["dns_experiment_failure"] == None
assert tk["dns_consistency"] == "consistent"
assert tk["control_failure"] == None
if "miniooni" in ooni_exe:
assert tk["http_experiment_failure"] == "ssl_invalid_certificate"
else:
assert "certificate verify failed" in tk["http_experiment_failure"]
assert tk["body_length_match"] == None
assert tk["body_proportion"] == 0
assert tk["status_code_match"] == None
assert tk["headers_match"] == None
assert tk["title_match"] == None
# The following strikes me as a measurement_kit bug. We are saying
# that all is good with a domain where actually we don't know why the
# control is failed and that is clearly not accessible according to
# our measurement of the domain (certificate expired).
#
# See <https://github.com/ooni/probe-engine/issues/858>.
if "miniooni" in ooni_exe:
assert tk["blocking"] == None
assert tk["accessible"] == None
else:
assert tk["blocking"] == False
assert tk["accessible"] == True
assert_status_flags_are(ooni_exe, tk, 16)
def webconnectivity_https_wrong_host(ooni_exe, outfile):
"""Test case where the hostname is wrong for the certificate"""
args = []
tk = execute_jafar_and_return_validated_test_keys(
ooni_exe,
outfile,
"-i https://wrong.host.badssl.com/ web_connectivity",
"webconnectivity_https_wrong_host",
args,
)
assert tk["dns_experiment_failure"] == None
assert tk["dns_consistency"] == "consistent"
assert tk["control_failure"] == None
if "miniooni" in ooni_exe:
assert tk["http_experiment_failure"] == "ssl_invalid_hostname"
else:
assert "certificate verify failed" in tk["http_experiment_failure"]
assert tk["body_length_match"] == None
assert tk["body_proportion"] == 0
assert tk["status_code_match"] == None
assert tk["headers_match"] == None
assert tk["title_match"] == None
# The following strikes me as a measurement_kit bug. We are saying
# that all is good with a domain where actually we don't know why the
# control is failed and that is clearly not accessible according to
# our measurement of the domain (wrong host for certificate).
#
# See <https://github.com/ooni/probe-engine/issues/858>.
if "miniooni" in ooni_exe:
assert tk["blocking"] == None
assert tk["accessible"] == None
else:
assert tk["blocking"] == False
assert tk["accessible"] == True
assert_status_flags_are(ooni_exe, tk, 16)
def webconnectivity_https_self_signed(ooni_exe, outfile):
"""Test case where the certificate is self signed"""
args = []
tk = execute_jafar_and_return_validated_test_keys(
ooni_exe,
outfile,
"-i https://self-signed.badssl.com/ web_connectivity",
"webconnectivity_https_self_signed",
args,
)
assert tk["dns_experiment_failure"] == None
assert tk["dns_consistency"] == "consistent"
assert tk["control_failure"] == None
if "miniooni" in ooni_exe:
assert tk["http_experiment_failure"] == "ssl_unknown_authority"
else:
assert "certificate verify failed" in tk["http_experiment_failure"]
assert tk["body_length_match"] == None
assert tk["body_proportion"] == 0
assert tk["status_code_match"] == None
assert tk["headers_match"] == None
assert tk["title_match"] == None
# The following strikes me as a measurement_kit bug. We are saying
# that all is good with a domain where actually we don't know why the
# control is failed and that is clearly not accessible according to
# our measurement of the domain (self signed certificate).
#
# See <https://github.com/ooni/probe-engine/issues/858>.
if "miniooni" in ooni_exe:
assert tk["blocking"] == None
assert tk["accessible"] == None
else:
assert tk["blocking"] == False
assert tk["accessible"] == True
assert_status_flags_are(ooni_exe, tk, 16)
def webconnectivity_https_untrusted_root(ooni_exe, outfile):
"""Test case where the certificate has an untrusted root"""
args = []
tk = execute_jafar_and_return_validated_test_keys(
ooni_exe,
outfile,
"-i https://untrusted-root.badssl.com/ web_connectivity",
"webconnectivity_https_untrusted_root",
args,
)
assert tk["dns_experiment_failure"] == None
assert tk["dns_consistency"] == "consistent"
assert tk["control_failure"] == None
if "miniooni" in ooni_exe:
assert tk["http_experiment_failure"] == "ssl_unknown_authority"
else:
assert "certificate verify failed" in tk["http_experiment_failure"]
assert tk["body_length_match"] == None
assert tk["body_proportion"] == 0
assert tk["status_code_match"] == None
assert tk["headers_match"] == None
assert tk["title_match"] == None
# The following strikes me as a measurement_kit bug. We are saying
# that all is good with a domain where actually we don't know why the
# control is failed and that is clearly not accessible according to
# our measurement of the domain (untrusted root certificate).
#
# See <https://github.com/ooni/probe-engine/issues/858>.
if "miniooni" in ooni_exe:
assert tk["blocking"] == None
assert tk["accessible"] == None
else:
assert tk["blocking"] == False
assert tk["accessible"] == True
assert_status_flags_are(ooni_exe, tk, 16)
def webconnectivity_dns_blocking_nxdomain(ooni_exe, outfile):
"""Test case where there is blocking using NXDOMAIN"""
args = [
"-iptables-hijack-dns-to",
"127.0.0.1:53",
"-dns-proxy-block",
"example.com",
]
tk = execute_jafar_and_return_validated_test_keys(
ooni_exe,
outfile,
"-i https://example.com/ web_connectivity",
"webconnectivity_dns_blocking_nxdomain",
args,
)
# The following seems a bug in MK where we don't properly record the
# actual error that occurred when performing the DNS experiment.
#
# See <https://github.com/measurement-kit/measurement-kit/issues/1931>.
if "miniooni" in ooni_exe:
assert tk["dns_experiment_failure"] == "dns_nxdomain_error"
else:
assert tk["dns_experiment_failure"] == None
assert tk["dns_consistency"] == "inconsistent"
assert tk["control_failure"] == None
if "miniooni" in ooni_exe:
assert tk["http_experiment_failure"] == None
else:
assert tk["http_experiment_failure"] == "dns_lookup_error"
assert tk["body_length_match"] == None
assert tk["body_proportion"] == 0
assert tk["status_code_match"] == None
assert tk["headers_match"] == None
assert tk["title_match"] == None
assert tk["blocking"] == "dns"
assert tk["accessible"] == False
assert_status_flags_are(ooni_exe, tk, 2080)
def webconnectivity_https_unknown_authority_with_inconsistent_dns(ooni_exe, outfile):
"""Test case where the DNS is sending us towards a website where
we're served an invalid certificate"""
args = [
"-iptables-hijack-dns-to",
"127.0.0.1:53",
"-dns-proxy-hijack",
"example.org",
"-bad-proxy-address-tls",
"127.0.0.1:443",
"-tls-proxy-address",
"127.0.0.1:4114",
]
tk = execute_jafar_and_return_validated_test_keys(
ooni_exe,
outfile,
"-i https://example.org/ web_connectivity",
"webconnectivity_https_unknown_authority_with_inconsistent_dns",
args,
)
assert tk["dns_experiment_failure"] == None
assert tk["dns_consistency"] == "inconsistent"
assert tk["control_failure"] == None
if "miniooni" in ooni_exe:
assert tk["http_experiment_failure"] == "ssl_unknown_authority"
else:
assert "certificate verify failed" in tk["http_experiment_failure"]
assert tk["body_length_match"] == None
assert tk["body_proportion"] == 0
assert tk["status_code_match"] == None
assert tk["headers_match"] == None
assert tk["title_match"] == None
assert tk["blocking"] == "dns"
assert tk["accessible"] == False
assert_status_flags_are(ooni_exe, tk, 9248)
def main():
if len(sys.argv) != 2:
sys.exit("usage: %s /path/to/ooniprobelegacy-like/binary" % sys.argv[0])
outfile = "webconnectivity.jsonl"
ooni_exe = sys.argv[1]
tests = [
webconnectivity_https_ok_with_control_failure,
webconnectivity_http_ok_with_control_failure,
webconnectivity_transparent_http_proxy,
webconnectivity_transparent_https_proxy,
webconnectivity_dns_hijacking,
webconnectivity_control_unreachable_and_using_http,
webconnectivity_nonexistent_domain,
webconnectivity_tcpip_blocking_with_consistent_dns,
webconnectivity_tcpip_blocking_with_inconsistent_dns,
webconnectivity_http_connection_refused_with_consistent_dns,
webconnectivity_http_connection_reset_with_consistent_dns,
webconnectivity_http_nxdomain_with_consistent_dns,
webconnectivity_http_eof_error_with_consistent_dns,
webconnectivity_http_generic_timeout_error_with_consistent_dns,
webconnectivity_http_connection_reset_with_inconsistent_dns,
webconnectivity_http_successful_website,
webconnectivity_https_successful_website,
webconnectivity_http_diff_with_inconsistent_dns,
webconnectivity_http_diff_with_consistent_dns,
webconnectivity_https_expired_certificate,
webconnectivity_https_wrong_host,
webconnectivity_https_self_signed,
webconnectivity_https_untrusted_root,
webconnectivity_dns_blocking_nxdomain,
webconnectivity_https_unknown_authority_with_inconsistent_dns,
]
for test in tests:
test(ooni_exe, outfile)
time.sleep(7)
if __name__ == "__main__":
main()