Skip to content

Commit

Permalink
feat(webconnectivityqa): convert more jafar test cases (ooni#1241)
Browse files Browse the repository at this point in the history
## Checklist

- [x] I have read the [contribution
guidelines](https://github.com/ooni/probe-cli/blob/master/CONTRIBUTING.md)
- [x] reference issue for this pull request:
ooni/probe#1803
- [x] if you changed anything related to how experiments work and you
need to reflect these changes in the ooni/spec repository, please link
to the related ooni/spec pull request: N/A
- [x] if you changed code inside an experiment, make sure you bump its
version number: N/A

## Description

This diff moves test cases from QA/webconnectivity.py to the
./internal/webconnectivityqa package.
  • Loading branch information
bassosimone authored and Murphy-OrangeMud committed Feb 13, 2024
1 parent 945c5f8 commit a911269
Show file tree
Hide file tree
Showing 10 changed files with 361 additions and 124 deletions.
105 changes: 0 additions & 105 deletions QA/webconnectivity.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,108 +47,6 @@ def assert_status_flags_are(ooni_exe, tk, desired):
assert tk["x_status"] == desired


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_diff_with_inconsistent_dns(ooni_exe, outfile):
"""Test case where we get an http-diff and the DNS is inconsistent"""
args = [
Expand Down Expand Up @@ -400,9 +298,6 @@ def main():
outfile = "webconnectivity.jsonl"
ooni_exe = sys.argv[1]
tests = [
webconnectivity_http_eof_error_with_consistent_dns,
webconnectivity_http_generic_timeout_error_with_consistent_dns,
webconnectivity_http_connection_reset_with_inconsistent_dns,
webconnectivity_http_diff_with_inconsistent_dns,
webconnectivity_http_diff_with_consistent_dns,
webconnectivity_https_expired_certificate,
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ require (
github.com/mitchellh/go-wordwrap v1.0.1
github.com/montanaflynn/stats v0.7.1
github.com/ooni/go-libtor v1.1.8
github.com/ooni/netem v0.0.0-20230905212743-4889f9501fcf
github.com/ooni/netem v0.0.0-20230905233956-4c9ebf9611c6
github.com/ooni/oocrypto v0.5.3
github.com/ooni/oohttp v0.6.3
github.com/ooni/probe-assets v0.18.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -483,8 +483,8 @@ github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl
github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU=
github.com/ooni/go-libtor v1.1.8 h1:Wo3V3DVTxl5vZdxtQakqYP+DAHx7pPtAFSl1bnAa08w=
github.com/ooni/go-libtor v1.1.8/go.mod h1:q1YyLwRD9GeMyeerVvwc0vJ2YgwDLTp2bdVcrh/JXyI=
github.com/ooni/netem v0.0.0-20230905212743-4889f9501fcf h1:dkrCLZASi2HCcAT1TLNFX+EpONGEeYt5VIZKj88aVZU=
github.com/ooni/netem v0.0.0-20230905212743-4889f9501fcf/go.mod h1:3LJOzTIu2O4ADDJN2ILG4ViJOqyH/u9fKY8QT2Rma8Y=
github.com/ooni/netem v0.0.0-20230905233956-4c9ebf9611c6 h1:0GHOnir3Dy4BIsoYTKPxPa3ixNOBxx1VSWsV9qxCfN8=
github.com/ooni/netem v0.0.0-20230905233956-4c9ebf9611c6/go.mod h1:3LJOzTIu2O4ADDJN2ILG4ViJOqyH/u9fKY8QT2Rma8Y=
github.com/ooni/oocrypto v0.5.3 h1:CAb0Ze6q/EWD1PRGl9KqpzMfkut4O3XMaiKYsyxrWOs=
github.com/ooni/oocrypto v0.5.3/go.mod h1:HjEQ5pQBl6btcWgAsKKq1tFo8CfBrZu63C/vPAUGIDk=
github.com/ooni/oohttp v0.6.3 h1:MHydpeAPU/LSDSI/hIFJwZm4afBhd2Yo+rNxxFdeMCY=
Expand Down
3 changes: 3 additions & 0 deletions internal/experiment/webconnectivity/qa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ func TestQA(t *testing.T) {
if (tc.Flags & webconnectivityqa.TestCaseFlagNoV04) != 0 {
t.Skip("this test case cannot run on Web Connectivity v0.4")
}
if testing.Short() && tc.LongTest {
t.Skip("skip test in short mode")
}
measurer := NewExperimentMeasurer(Config{})
if err := webconnectivityqa.RunTestCase(measurer, tc); err != nil {
t.Fatal(err)
Expand Down
3 changes: 3 additions & 0 deletions internal/experiment/webconnectivitylte/qa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ func TestQA(t *testing.T) {
if (tc.Flags & webconnectivityqa.TestCaseFlagNoLTE) != 0 {
t.Skip("this test case cannot run on Web Connectivity LTE")
}
if testing.Short() && tc.LongTest {
t.Skip("skip test in short mode")
}
measurer := NewExperimentMeasurer(&Config{})
if err := webconnectivityqa.RunTestCase(measurer, tc); err != nil {
t.Fatal(err)
Expand Down
154 changes: 154 additions & 0 deletions internal/experiment/webconnectivityqa/redirect.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,3 +188,157 @@ func redirectWithConsistentDNSAndThenNXDOMAIN() *TestCase {
},
}
}

// redirectWithConsistentDNSAndThenEOFForHTTP is a scenario where the redirect
// works but then there's connection EOF for an HTTP URL.
func redirectWithConsistentDNSAndThenEOFForHTTP() *TestCase {
return &TestCase{
Name: "redirectWithConsistentDNSAndThenEOFForHTTP",
Flags: 0,
Input: "https://bit.ly/32447",
Configure: func(env *netemx.QAEnv) {

// make sure we cannot HTTP round trip
env.DPIEngine().AddRule(&netem.DPICloseConnectionForString{
Logger: log.Log,
ServerIPAddress: netemx.AddressWwwExampleCom,
ServerPort: 80,
String: "www.example.com",
})

// make sure we cannot TLS handshake
env.DPIEngine().AddRule(&netem.DPICloseConnectionForTLSSNI{
Logger: log.Log,
SNI: "www.example.com",
})

},
ExpectErr: false,
ExpectTestKeys: &testKeys{
DNSExperimentFailure: nil,
DNSConsistency: "consistent",
HTTPExperimentFailure: "eof_error",
XStatus: 8448, // StatusExperimentHTTP | StatusAnomalyReadWrite
XDNSFlags: 0,
XBlockingFlags: 8, // analysisFlagHTTPBlocking
Accessible: false,
Blocking: "http-failure",
},
}
}

// redirectWithConsistentDNSAndThenEOFForHTTPS is a scenario where the redirect
// works but then there's connection EOF for an HTTPS URL.
func redirectWithConsistentDNSAndThenEOFForHTTPS() *TestCase {
return &TestCase{
Name: "redirectWithConsistentDNSAndThenEOFForHTTPS",
Flags: TestCaseFlagNoLTE, // BUG: LTE thinks this website is accessible (WTF?!)
Input: "https://bit.ly/21645",
Configure: func(env *netemx.QAEnv) {

// make sure we cannot connect to the example domain on port 80
env.DPIEngine().AddRule(&netem.DPICloseConnectionForString{
Logger: log.Log,
ServerIPAddress: netemx.AddressWwwExampleCom,
ServerPort: 80,
String: "www.example.com",
})

// make sure we cannot connect to the example domain on port 443
env.DPIEngine().AddRule(&netem.DPICloseConnectionForTLSSNI{
Logger: log.Log,
SNI: "www.example.com",
})

},
ExpectErr: false,
ExpectTestKeys: &testKeys{
DNSExperimentFailure: nil,
DNSConsistency: "consistent",
HTTPExperimentFailure: "eof_error",
XStatus: 8448, // StatusExperimentHTTP | StatusAnomalyReadWrite
XDNSFlags: 0,
XBlockingFlags: 32, // analysisFlagSuccess
Accessible: false,
Blocking: "http-failure",
},
}
}

// redirectWithConsistentDNSAndThenTimeoutForHTTP is a scenario where the redirect
// works but then there's connection refused for an HTTP URL.
func redirectWithConsistentDNSAndThenTimeoutForHTTP() *TestCase {
return &TestCase{
Name: "redirectWithConsistentDNSAndThenTimeoutForHTTP",
Flags: 0,
Input: "https://bit.ly/32447",
LongTest: true,
Configure: func(env *netemx.QAEnv) {

// make sure we cannot perform the round trip
env.DPIEngine().AddRule(&netem.DPIDropTrafficForString{
Logger: log.Log,
ServerIPAddress: netemx.AddressWwwExampleCom,
ServerPort: 80,
String: "www.example.com",
})

// make sure we cannot TLS handshake
env.DPIEngine().AddRule(&netem.DPIDropTrafficForTLSSNI{
Logger: log.Log,
SNI: "www.example.com",
})

},
ExpectErr: false,
ExpectTestKeys: &testKeys{
DNSExperimentFailure: nil,
DNSConsistency: "consistent",
HTTPExperimentFailure: "generic_timeout_error",
XStatus: 8704, // StatusExperimentHTTP | StatusAnomalyUnknown
XDNSFlags: 0,
XBlockingFlags: 8, // analysisFlagHTTPBlocking
Accessible: false,
Blocking: "http-failure",
},
}
}

// redirectWithConsistentDNSAndThenTimeoutForHTTPS is a scenario where the redirect
// works but then there's connection refused for an HTTPS URL.
func redirectWithConsistentDNSAndThenTimeoutForHTTPS() *TestCase {
return &TestCase{
Name: "redirectWithConsistentDNSAndThenTimeoutForHTTPS",
Flags: TestCaseFlagNoLTE, // BUG: LTE thinks this website is accessible (WTF?!)
Input: "https://bit.ly/21645",
LongTest: true,
Configure: func(env *netemx.QAEnv) {

// make sure we cannot HTTP round trip
env.DPIEngine().AddRule(&netem.DPIDropTrafficForString{
Logger: log.Log,
ServerIPAddress: netemx.AddressWwwExampleCom,
ServerPort: 80,
String: "www.example.com",
})

// make sure we cannot TLS handshake
env.DPIEngine().AddRule(&netem.DPIDropTrafficForTLSSNI{
Logger: log.Log,
SNI: "www.example.com",
})

},
ExpectErr: false,
ExpectTestKeys: &testKeys{
DNSExperimentFailure: nil,
DNSConsistency: "consistent",
HTTPExperimentFailure: "generic_timeout_error",
XStatus: 8704, // StatusExperimentHTTP | StatusAnomalyUnknown
XDNSFlags: 0,
XBlockingFlags: 32, // analysisFlagSuccess
Accessible: false,
Blocking: "http-failure",
},
}
}
67 changes: 67 additions & 0 deletions internal/experiment/webconnectivityqa/redirect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"net"
"net/http"
"testing"
"time"

"github.com/apex/log"
"github.com/ooni/probe-cli/v3/internal/netemx"
Expand Down Expand Up @@ -114,3 +115,69 @@ func TestRedirectWithConsistentDNSAndThenNXDOMAIN(t *testing.T) {
})
}
}

func TestRedirectWithConsistentDNSAndThenEOF(t *testing.T) {
testcases := []*TestCase{
redirectWithConsistentDNSAndThenEOFForHTTP(),
redirectWithConsistentDNSAndThenEOFForHTTPS(),
}

for _, tc := range testcases {
t.Run(tc.Name, func(t *testing.T) {
env := netemx.MustNewScenario(netemx.InternetScenario)
tc.Configure(env)

env.Do(func() {
urls := []string{"http://www.example.com/", "https://www.example.com/"}

for _, URL := range urls {
t.Run(fmt.Sprintf("for URL %s", URL), func(t *testing.T) {
client := netxlite.NewHTTPClientStdlib(log.Log)
req := runtimex.Try1(http.NewRequest("GET", URL, nil))
resp, err := client.Do(req)
if err == nil || err.Error() != netxlite.FailureEOFError {
t.Fatal("unexpected err", err)
}
if resp != nil {
t.Fatal("expected nil resp")
}
})
}
})
})
}
}

func TestRedirectWithConsistentDNSAndThenTimeout(t *testing.T) {
testcases := []*TestCase{
redirectWithConsistentDNSAndThenTimeoutForHTTP(),
redirectWithConsistentDNSAndThenTimeoutForHTTPS(),
}

for _, tc := range testcases {
t.Run(tc.Name, func(t *testing.T) {
env := netemx.MustNewScenario(netemx.InternetScenario)
tc.Configure(env)

env.Do(func() {
urls := []string{"http://www.example.com/", "https://www.example.com/"}

for _, URL := range urls {
t.Run(fmt.Sprintf("for URL %s", URL), func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
client := netxlite.NewHTTPClientStdlib(log.Log)
req := runtimex.Try1(http.NewRequestWithContext(ctx, "GET", URL, nil))
resp, err := client.Do(req)
if err == nil || err.Error() != netxlite.FailureGenericTimeoutError {
t.Fatal("unexpected err", err)
}
if resp != nil {
t.Fatal("expected nil resp")
}
})
}
})
})
}
}
Loading

0 comments on commit a911269

Please sign in to comment.